CVE-2022-25231 (published 2022-08-23T05:15Z)

The package node-opcua before 2.74.0 are vulnerable to Denial of Service (DoS) by sending a specifically crafted OPC UA message with a special OPC UA NodeID, when the requested memory allocation exceeds the v8’s memory limit.

CVSS v3 base score: 7.5, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-770
cwe_name: Allocation of Resources Without Limits or Throttling
description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
extended_description: Code frequently has to work with limited resources, so programmers must be careful to ensure that resources are not consumed too quickly, or too easily.  Without use of quotas, resource limits, or other protection mechanisms, it can be easy for an attacker to consume many resources by rapidly making many requests, or causing larger resources to be used than is needed. When too many resources are allocated, or if a single resource is too large, then it can prevent the code from working correctly, possibly leading to a denial of service.
         

Fix information:
repo_url: https://github.com/node-opcua/node-opcua
commit_details: repo_url: https://github.com/node-opcua/node-opcua
author: Etienne Rossignon
author_date: 2022-08-13 00:06:23+02:00
msg: server: use ServiceFault on request error
repository: repo_url: https://github.com/node-opcua/node-opcua
repo_name: node-opcua/node-opcua
description: Unlocking the Full Potential of OPC UA with Typescript and NodeJS - http://node-opcua.github.io/
repo_language: TypeScript
owner: node-opcua
file_changes: filename: client_session_impl.ts
change_type: ModificationType.MODIFY
diff: @@ -10,7 +10,7 @@ import {
     ExtraDataTypeManager,
     getExtensionObjectConstructor,
     getExtraDataTypeManager,
-    promoteOpaqueStructure,
+    promoteOpaqueStructure
 } from "node-opcua-client-dynamic-extension-object";
 import { Certificate, Nonce } from "node-opcua-crypto";
 import { attributeNameById, BrowseDirection, LocalizedTextLike } from "node-opcua-data-model";
@@ -82,7 +82,13 @@ import {
 import { WriteRequest, WriteResponse, WriteValue } from "node-opcua-service-write";
 import { StatusCode, StatusCodes, Callback, CallbackT } from "node-opcua-status-code";
 import { ErrorCallback } from "node-opcua-status-code";
-import { BrowseNextRequest, BrowseNextResponse, HistoryReadValueIdOptions, WriteValueOptions } from "node-opcua-types";
+import {
+    BrowseNextRequest,
+    BrowseNextResponse,
+    HistoryReadValueIdOptions,
+    ServiceFault,
+    WriteValueOptions
+} from "node-opcua-types";
 import { buffer_ellipsis, getFunctionParameterNames, isNullOrUndefined, lowerFirstLetter } from "node-opcua-utils";
 import { DataType, Variant, VariantLike } from "node-opcua-variant";
 
@@ -1540,6 +1546,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
             if (err && err.message.match(/BadSessionIdInvalid/) && request.constructor.name !== "ActivateSessionRequest") {
                 debugLog("Transaction on Invalid Session ", request.constructor.name);
                 request.requestHeader.requestHandle = requestHandleNotSetValue;
+                warningLog("client is now attempting to recreate a session");
                 this.recreate_session_and_reperform_transaction(request, callback);
                 return;
             }
@@ -1595,6 +1602,7 @@ export class ClientSessionImpl extends EventEmitter implements ClientSession {
                 }
                 return callback(err);
             }
+
             /* istanbul ignore next */
             if (!response) {
                 return callback(new Error("internal Error"));

programming_language: TypeScript
filename: client_subscription_impl.ts
change_type: ModificationType.MODIFY
diff: @@ -471,8 +471,12 @@ export class ClientSubscriptionImpl extends EventEmitter implements ClientSubscr
         });
         session.setTriggering(setTriggeringRequest, (err: Error | null, response?: SetTriggeringResponse) => {
             if (err) {
-                // use soft error, no exceptions
-                return callback(null, response);
+                if (response) {
+                    // use soft error, no exceptions
+                    return callback(null, response);
+                } else {
+                    return callback(err);
+                }
             }
             // istanbul ignore next
             if (!response) {

programming_language: TypeScript
filename: u_test_discovery_server.ts
change_type: ModificationType.MODIFY
diff: @@ -18,7 +18,8 @@ import {
     StatusCodes,
     RegisterServerMethod,
     makeApplicationUrn,
-    OPCUADiscoveryServer
+    OPCUADiscoveryServer,
+    ServiceFault
 } from "node-opcua";
 import { readCertificate, exploreCertificate } from "node-opcua-crypto";
 import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
@@ -132,13 +133,15 @@ export function t(test: any) {
                 }
             });
 
-            function check_response(err: Error | null, response: any): void {
-                should.not.exist(err);
+            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);
                 //xx console.log(response.toString());
-                response.responseHeader.serviceResult.should.eql(StatusCodes.BadDiscoveryUrlMissing);
+                (err as any).response.responseHeader.serviceResult.should.eql(StatusCodes.BadDiscoveryUrlMissing);
             }
 
-            await send_registered_server_request(discoveryServerEndpointUrl, request, check_response);
+            await send_registered_server_request(discoveryServerEndpointUrl, request, check_error_response);
         });
 
         it("DISCO1-2 should fail to register server to the discover server if server type is Client (BadInvalidArgument)", async () => {
@@ -161,13 +164,15 @@ export function t(test: any) {
                 }
             });
 
-            function check_response(err: Error | null, response: any) {
-                should.not.exist(err);
+            function check_error_response(err: Error | null, response: any) {
+                should.exist(err);
+                should.not.exist(response);
                 //xx debugLog(response.toString());
-                response.responseHeader.serviceResult.should.eql(StatusCodes.BadInvalidArgument);
+                (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_response);
+            await send_registered_server_request(discoveryServerEndpointUrl, request, check_error_response);
         });
 
         it("DISCO1-3 should fail to register server to the discover server if server name array is empty (BadServerNameMissing)", async () => {
@@ -190,12 +195,14 @@ export function t(test: any) {
                 }
             });
 
-            function check_response(err: Error | null, response: any) {
-                should.not.exist(err);
-                response.responseHeader.serviceResult.should.eql(StatusCodes.BadServerNameMissing);
+            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_response);
+            await send_registered_server_request(discoveryServerEndpointUrl, request, check_error_response);
         });
     });
 
@@ -207,7 +214,8 @@ export function t(test: any) {
         let server: OPCUAServer;
 
         before(() => {
-            OPCUAServer.registry.count().should.eql(0);1162
+            OPCUAServer.registry.count().should.eql(0);
+            1162;
         });
 
         after(() => {

programming_language: TypeScript
filename: u_test_e2e_BrowseRequest.js
change_type: ModificationType.MODIFY
diff: @@ -164,10 +164,10 @@ module.exports = function(test) {
                     const browseNextRequest = new BrowseNextRequest({
                         continuationPoints: null
                     });
-                    g_session.performMessageTransaction(browseNextRequest, function(err, response) {
+                    g_session.performMessageTransaction(browseNextRequest, function(err, _response) {
                         err.message.should.match(/BadNothingToDo/);
                         // console.log(response.toString());
-                        response.responseHeader.serviceResult.should.equal(StatusCodes.BadNothingToDo);
+                        err.response.responseHeader.serviceResult.should.equal(StatusCodes.BadNothingToDo);
                         callback();
                     });
                 }

programming_language: JavaScript
filename: u_test_e2e_SubscriptionUseCase.js
change_type: ModificationType.MODIFY
diff: @@ -39,10 +39,10 @@ const {
     Variant,
     Subscription,
     SubscriptionState,
-    installSessionLogging
+    installSessionLogging,
+    ServiceFault
 } = require("node-opcua");
 
-
 const { make_debugLog, checkDebugFlag } = require("node-opcua-debug");
 
 const {
@@ -726,7 +726,8 @@ module.exports = function (test) {
                                 },
                                 function (callback) {
                                     function publish_callback(err, response) {
-                                        should.exist(response);
+                                        should.not.exist(response);
+                                        err.response.should.be.instanceOf(ServiceFault);
                                         should(err.message).match(/BadNoSubscription/);
                                     }
 
@@ -825,7 +826,8 @@ module.exports = function (test) {
                                         subscriptionIds: []
                                     },
                                     function (err, response) {
-                                        should.exist(response);
+                                        should.not.exist(response);
+                                        should.exist(err);
                                         err.message.should.match(/BadNothingToDo/);
                                         callback();
                                     }
@@ -1518,7 +1520,8 @@ module.exports = function (test) {
                     });
                     session.createMonitoredItems(createMonitoredItemsRequest, function (err, createMonitoredItemsResponse) {
                         should(err.message).match(/BadNothingToDo/);
-                        createMonitoredItemsResponse.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
+                        should.not.exist(createMonitoredItemsResponse);
+                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                         callback();
                     });
                 },
@@ -1581,7 +1584,8 @@ module.exports = function (test) {
                     });
                     session.modifyMonitoredItems(modifyMonitoredItemsRequest, function (err, modifyMonitoredItemsResponse) {
                         should(err.message).match(/BadNothingToDo/);
-                        modifyMonitoredItemsResponse.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
+                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
+                        should.not.exist(modifyMonitoredItemsResponse);
                         callback();
                     });
                 },
@@ -1600,7 +1604,8 @@ module.exports = function (test) {
                     });
                     session.deleteMonitoredItems(deleteMonitoredItemsRequest, function (err, deleteMonitoredItemsResponse) {
                         should(err.message).match(/BadNothingToDo/);
-                        deleteMonitoredItemsResponse.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
+                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
+                        should.not.exist(deleteMonitoredItemsResponse);
                         callback();
                     });
                 },
@@ -1793,6 +1798,7 @@ module.exports = function (test) {
                             requestedPublishingInterval: -1
                         },
                         function (err, createSubscriptionResponse) {
+                            should.not.exist(err);
                             createSubscriptionResponse.revisedPublishingInterval.should.be.greaterThan(10);
 
                             inner_done(err);
@@ -1903,7 +1909,6 @@ module.exports = function (test) {
 
             installSessionLogging(server);
 
-
             endpointUrl = test.endpointUrl;
             temperatureVariableId = server.temperatureVariableId;
 
@@ -2139,13 +2144,13 @@ module.exports = function (test) {
                             })
                         ],
                         (err) => {
-                            tracelog("inner", err ? err.message: "");
+                            tracelog("inner", err ? err.message : "");
                             inner_done(err);
                         }
                     );
                 },
                 (err) => {
-                    tracelog("done", err ? err.message: "");
+                    tracelog("done", err ? err.message : "");
                     done(err);
                 }
             );
@@ -4064,8 +4069,9 @@ module.exports = function (test) {
                     });
                     g_session.republish(request, function (err, response) {
                         should.exist(err);
-                        response.should.be.instanceof(RepublishResponse);
-                        response.responseHeader.serviceResult.should.eql(StatusCodes.BadMessageNotAvailable);
+                        should.not.exist(response);
+                        err.response.should.be.instanceof(ServiceFault);
+                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadMessageNotAvailable);
                         done();
                     });
                 }, done);
@@ -4081,8 +4087,9 @@ module.exports = function (test) {
                     });
                     g_session.republish(request, function (err, response) {
                         should.exist(err);
-                        response.should.be.instanceof(RepublishResponse);
-                        response.responseHeader.serviceResult.should.eql(StatusCodes.BadSubscriptionIdInvalid);
+                        should.not.exist(response);
+                        err.response.should.be.instanceof(ServiceFault);
+                        err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadSubscriptionIdInvalid);
                         done();
                     });
                 }, done);

programming_language: JavaScript
filename: u_test_e2e_Subscription_Transfer.js
change_type: ModificationType.MODIFY
diff: @@ -456,10 +456,17 @@ module.exports = function (test) {
                         const response1 = spy_publish_session2.getCall(1).args[1];
                         const response2 = spy_publish_session2.getCall(2).args[1];
                         const response3 = spy_publish_session2.getCall(3).args[1];
-
-                        response1.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
-                        response2.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
-                        response3.responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
+                        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);
                         //xx console.log(response1.toString())
                         //xx console.log(response2.toString())
                         //xx console.log(response3.toString())

programming_language: JavaScript
filename: u_test_e2e_browse_read.js
change_type: ModificationType.MODIFY
diff: @@ -29,6 +29,8 @@ const fail_fast_connectivity_strategy = {
 const describe = require("node-opcua-leak-detector").describeWithLeakDetector;
 module.exports = function (test) {
     describe("Browse-Read-Write Services", function () {
+
+        /** @type {ClientSession} */
         let g_session = null;
 
         // use fail fast connectionStrategy
@@ -170,7 +172,7 @@ module.exports = function (test) {
 
             g_session.read(nodesToRead, function (err, dataValues) {
                 if (err) {
-                    const response = dataValues;
+                    const response = err.response;
                     //dataValues.length.should.be(1);
                     response.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
                     done();

programming_language: JavaScript
filename: u_test_e2e_registerNodes.js
change_type: ModificationType.MODIFY
diff: @@ -2,20 +2,11 @@
 const should = require("should");
 const async = require("async");
 
+const { OPCUAClient, StatusCodes, UnregisterNodesRequest, RegisterNodesRequest, DataType, AttributeIds, ServiceFault } = require("node-opcua");
 const { perform_operation_on_client_session } = require("../../test_helpers/perform_operation_on_client_session");
 
-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;
-
 module.exports = function (test) {
-
     describe("end-to-end testing registerNodes", function () {
-
         let client, endpointUrl;
 
         beforeEach(function (done) {
@@ -30,135 +21,147 @@ module.exports = function (test) {
         });
 
         it("should register nodes - BadNothingToDo", function (done) {
-
-            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: []
+                    });
+                    session.performMessageTransaction(request, function (err /*, response*/) {
+                        err.message.should.match(/BadNothingToDo/);
+                        inner_done();
+                    });
+                },
+                done
+            );
         });
 
         it("should register nodes - Good", function (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 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
+            );
         });
 
         it("should unregister nodes - BadNothingToDo", function (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: []
+                    });
+                    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
+            );
         });
 
         it("should unregister nodes - Good", function (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 request = new UnregisterNodesRequest({
+                        nodesToUnregister: ["ns=0;i=1"]
+                    });
+                    session.performMessageTransaction(request, function (err, response) {
+                        should.exist(response);
+                        should.not.exist(err);
+                        inner_done();
+                    });
+                },
+                done
+            );
         });
 
-
         it("should register nodes and provide a alias that can be used on all operations", function (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);
+            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();
                             }
-                            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);
+                        ],
+                        inner_done
+                    );
+                },
+                done
+            );
         });
     });
 };

programming_language: JavaScript
filename: u_test_e2e_set_triggering.ts
change_type: ModificationType.MODIFY
diff: @@ -1,3 +1,4 @@
+/* eslint-disable max-statements */
 // tslint:disable: no-shadowed-variable
 // tslint:disable: no-console
 import {
@@ -29,6 +30,7 @@ import {
     OPCUAClient,
     Range,
     ServerSidePublishEngine,
+    ServiceFault,
     SetTriggeringRequestOptions,
     StatusCode,
     StatusCodes,
@@ -241,16 +243,14 @@ export function t(test: any) {
 
             try {
                 const result = await subscription.setTriggering(t, [], []);
-
-                //  console.log(result.toString());
-
-                result.removeResults?.length.should.eql(0);
-                result.addResults?.length.should.eql(0);
-                result.responseHeader.serviceResult.should.eql(StatusCodes.BadNothingToDo);
+                console.log(result.toString());
             } catch (err) {
                 _err = err as Error;
             }
-            should.not.exist(_err, "not expecting any exception");
+            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);
 
             /*
              */

programming_language: TypeScript
filename: u_test_e2e_test_accessing_service_before_session_is_activated.js
change_type: ModificationType.MODIFY
diff: @@ -29,6 +29,7 @@ module.exports = function(test) {
 
             let session1;
             let last_response;
+            let last_response_err;
             let activate_error;
             async.series([
 
@@ -58,6 +59,7 @@ module.exports = function(test) {
                         console.log(err ? err.toString() : "null");
                         console.log(response ? response.toString() : "null");
                         last_response = response;
+                        last_response_err = err;
                         callback();
                     });
                 },
@@ -81,7 +83,7 @@ module.exports = function(test) {
                 if (err) {
                     return done(err);
                 }
-                last_response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionNotActivated);
+                last_response_err.response.responseHeader.serviceResult.should.eql(StatusCodes.BadSessionNotActivated);
                 should.exist(activate_error, 
                     "Activate Session should return an error if there has been an attempt to use it before being activated");
                 activate_error.message.should.match(/BadSessionIdInvalid|BadSessionClosed/);

programming_language: JavaScript
filename: u_test_e2e_transfer_session.js
change_type: ModificationType.MODIFY
diff: @@ -1,724 +1,712 @@
 "use strict";
+const path = require("path");
+const fs = require("fs");
 const should = require("should");
 const async = require("async");
 const _ = require("underscore");
-const path = require("path");
-const fs = require("fs");
 
-const { 
-  OPCUAClient, 
-  StatusCodes, 
-  PublishRequest,
-  CreateSubscriptionRequest,
-  CloseSessionRequest,
-  ReadRequest,
-  TimestampsToReturn, 
-  MessageSecurityMode,
-  SecurityPolicy,
-  UserNameIdentityToken
-} = require("node-opcua");
 const {
-  readCertificate,
-  readCertificateRevocationList
-} = require("node-opcua-crypto");
+    OPCUAClient,
+    StatusCodes,
+    PublishRequest,
+    CreateSubscriptionRequest,
+    CloseSessionRequest,
+    ReadRequest,
+    TimestampsToReturn,
+    MessageSecurityMode,
+    SecurityPolicy,
+    UserNameIdentityToken,
+    ServiceFault,
+    ReadResponse
+} = require("node-opcua");
+const { readCertificate, readCertificateRevocationList } = require("node-opcua-crypto");
 
 const certificateFolder = path.join(__dirname, "../../../node-opcua-samples/certificates");
 fs.existsSync(certificateFolder).should.eql(true, "expecting certificate store at " + certificateFolder);
 
 function sendPublishRequest(session, callback) {
-  const publishRequest = new PublishRequest({});
-  session.performMessageTransaction(publishRequest, function(err, response) {
-    callback(err, response);
-  });
+    const publishRequest = new PublishRequest({});
+    session.performMessageTransaction(publishRequest, function (err, response) {
+        callback(err, response);
+    });
 }
 
 function createSubscription(session, callback) {
-  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);
-
+    const publishingInterval = 1000;
+    const createSubscriptionRequest = new CreateSubscriptionRequest({
+        requestedPublishingInterval: publishingInterval,
+        requestedLifetimeCount: 60,
+        requestedMaxKeepAliveCount: 10,
+        maxNotificationsPerPublish: 10,
+        publishingEnabled: true,
+        priority: 6
     });
 
-    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);
-
+    session.performMessageTransaction(createSubscriptionRequest, function (err /*, response*/) {
+        callback(err);
     });
+}
 
-    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();
-        }
+const describe = require("node-opcua-leak-detector").describeWithLeakDetector;
 
-      ], function final(err) {
-        client1.disconnect(function() {
-          done(err);
+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("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);
-    });
+        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
+            );
+        });
 
-    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();
-        }
+        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);
+                    });
+                }
+            );
+        });
 
-      ], done);
+        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
+            );
+        });
 
-    // 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);
+        function m(file) {
+            const p = path.join(certificateFolder, file);
+            if (!fs.existsSync(p)) {
+                console.error(" cannot find ", p);
             }
-            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();
+            return p;
         }
 
-      ], 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();
-        }
+        // 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
+            );
+        });
 
-      ], 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
+            );
+        });
     });
-
-  });
-
 };

programming_language: JavaScript
filename: client_secure_channel_layer.ts
change_type: ModificationType.MODIFY
diff: @@ -119,7 +119,8 @@ function process_request_callback(requestData: RequestData, err?: Error | null,
         err = new Error(" serviceResult = " + response.responseHeader.serviceResult.toString());
         //  "  returned by server \n response:" + response.toString() + "\n  request: " + request.toString());
         (err as any).response = response;
-        ((err as any).request = request), (response = undefined);
+        (err as any).request = request;
+        response = undefined;
     }
 
     const theCallbackFunction = requestData.callback;
@@ -1721,7 +1722,7 @@ export class ClientSecureChannelLayer extends EventEmitter {
         // istanbul ignore next
         if (!this.derivedKeys || !this.derivedKeys.derivedClientKeys) {
             errorLog("derivedKeys not set but security mode = ", MessageSecurityMode[this.securityMode]);
-            return null; // 
+            return null; //
             // throw new Error("internal error expecting valid derivedKeys while security mode is " + MessageSecurityMode[this.securityMode]);
         }
 

programming_language: TypeScript
filename: opcua_discovery_server.ts
change_type: ModificationType.MODIFY
diff: @@ -15,7 +15,7 @@ import { UAString } from "node-opcua-basic-types";
 import { makeApplicationUrn } from "node-opcua-common";
 import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
 import { extractFullyQualifiedDomainName, resolveFullyQualifiedDomainName } from "node-opcua-hostname";
-import { Message, Response, ServerSecureChannelLayer } from "node-opcua-secure-channel";
+import { Message, Response, ServerSecureChannelLayer, ServiceFault } from "node-opcua-secure-channel";
 import { OPCUABaseServer, OPCUABaseServerOptions, OPCUAServerEndPoint } from "node-opcua-server";
 
 import {
@@ -390,7 +390,7 @@ export class OPCUADiscoveryServer extends OPCUABaseServer {
 
         function sendError(statusCode: StatusCode): Response {
             debugLog(chalk.red("_on_RegisterServer(2)Request error"), statusCode.toString());
-            const response1 = new RegisterServerXResponse({
+            const response1 = new ServiceFault({
                 responseHeader: { serviceResult: statusCode }
             });
             return response1;

programming_language: TypeScript
filename: opcua_server.ts
change_type: ModificationType.MODIFY
diff: @@ -145,7 +145,8 @@ import {
     BuildInfoOptions,
     MonitoredItemCreateResult,
     IssuedIdentityToken,
-    BrowseResultOptions
+    BrowseResultOptions,
+    ServiceFault
 } from "node-opcua-types";
 import { DataType } from "node-opcua-variant";
 import { VariantArrayType } from "node-opcua-variant";
@@ -184,7 +185,7 @@ const warningLog = make_warningLog(__filename);
 const default_maxConnectionsPerEndpoint = 10;
 
 function g_sendError(channel: ServerSecureChannelLayer, message: Message, ResponseClass: any, statusCode: StatusCode): void {
-    const response = new ResponseClass({
+    const response = new ServiceFault({
         responseHeader: { serviceResult: statusCode }
     });
     return channel.send_response("MSG", response, message);
@@ -2233,7 +2234,7 @@ export class OPCUAServer extends OPCUABaseServer {
 
         function sendResponse(response1: Response) {
             try {
-                assert(response1 instanceof ResponseClass);
+                assert(response1 instanceof ResponseClass || response1 instanceof ServiceFault);
                 if (message.session) {
                     const counterName = ResponseClass.name.replace("Response", "");
                     message.session.incrementRequestTotalCounter(counterName);
@@ -3071,7 +3072,7 @@ export class OPCUAServer extends OPCUABaseServer {
             (session: ServerSession, sendResponse: (response: Response) => void, sendError: (statusCode: StatusCode) => void) => {
                 assert(session);
                 assert(session.publishEngine); // server.publishEngine doesn't exists, OPCUAServer has probably shut down already
-                session.publishEngine._on_PublishRequest(request, (request1: PublishRequest, response: PublishResponse) => {
+                session.publishEngine._on_PublishRequest(request, (_request1, response) => {
                     sendResponse(response);
                 });
             }
@@ -3180,16 +3181,20 @@ export class OPCUAServer extends OPCUABaseServer {
                     linksToAdd,
                     linksToRemove
                 );
-                const response = new SetTriggeringResponse({
-                    responseHeader: { serviceResult: statusCode },
-
-                    addResults,
-                    removeResults,
-                    addDiagnosticInfos: null,
-                    removeDiagnosticInfos: null
-                });
+                if (statusCode !== StatusCodes.Good) {
+                    const response = new ServiceFault({ responseHeader: { serviceResult: statusCode } });
+                    sendResponse(response);
+                } else {
+                    const response = new SetTriggeringResponse({
+                        responseHeader: { serviceResult: statusCode },
 
-                sendResponse(response);
+                        addResults,
+                        removeResults,
+                        addDiagnosticInfos: null,
+                        removeDiagnosticInfos: null
+                    });
+                    sendResponse(response);
+                }
             }
         );
     }
@@ -3390,11 +3395,8 @@ export class OPCUAServer extends OPCUABaseServer {
             message,
             channel,
             (session: ServerSession, sendResponse: (response: Response) => void, sendError: (statusCode: StatusCode) => void) => {
-                let response;
-
                 if (!request.nodesToRegister || request.nodesToRegister.length === 0) {
-                    response = new RegisterNodesResponse({ responseHeader: { serviceResult: StatusCodes.BadNothingToDo } });
-                    return sendResponse(response);
+                    return sendError(StatusCodes.BadNothingToDo);
                 }
                 if (this.engine.serverCapabilities.operationLimits.maxNodesPerRegisterNodes > 0) {
                     if (request.nodesToRegister.length > this.engine.serverCapabilities.operationLimits.maxNodesPerRegisterNodes) {
@@ -3410,7 +3412,7 @@ export class OPCUAServer extends OPCUABaseServer {
                 // NodeId from the request.
                 const registeredNodeIds = request.nodesToRegister.map((nodeId) => session.registerNode(nodeId));
 
-                response = new RegisterNodesResponse({
+                const response = new RegisterNodesResponse({
                     registeredNodeIds
                 });
                 sendResponse(response);
@@ -3427,13 +3429,10 @@ export class OPCUAServer extends OPCUABaseServer {
             message,
             channel,
             (session: ServerSession, sendResponse: (response: Response) => void, sendError: (statusCode: StatusCode) => void) => {
-                let response;
-
                 request.nodesToUnregister = request.nodesToUnregister || [];
 
                 if (!request.nodesToUnregister || request.nodesToUnregister.length === 0) {
-                    response = new UnregisterNodesResponse({ responseHeader: { serviceResult: StatusCodes.BadNothingToDo } });
-                    return sendResponse(response);
+                    return sendError(StatusCodes.BadNothingToDo);
                 }
 
                 if (this.engine.serverCapabilities.operationLimits.maxNodesPerRegisterNodes > 0) {
@@ -3446,7 +3445,7 @@ export class OPCUAServer extends OPCUABaseServer {
 
                 request.nodesToUnregister.map((nodeId: NodeId) => session.unRegisterNode(nodeId));
 
-                response = new UnregisterNodesResponse({});
+                const response = new UnregisterNodesResponse({});
                 sendResponse(response);
             }
         );

programming_language: TypeScript
filename: server_publish_engine.ts
change_type: ModificationType.MODIFY
diff: @@ -11,7 +11,7 @@ import { checkDebugFlag, make_debugLog } from "node-opcua-debug";
 import { ObjectRegistry } from "node-opcua-object-registry";
 import { StatusCode, StatusCodes } from "node-opcua-status-code";
 
-import { PublishRequest, PublishResponse, SubscriptionAcknowledgement } from "node-opcua-types";
+import { PublishRequest, PublishResponse, ServiceFault, SubscriptionAcknowledgement } from "node-opcua-types";
 import { Subscription } from "./server_subscription";
 import { SubscriptionState } from "./server_subscription";
 import { IServerSidePublishEngine, INotifMsg, IClosedOrTransferredSubscription } from "./i_server_side_publish_engine";
@@ -36,7 +36,7 @@ interface PublishData {
     request: PublishRequest;
     serverTimeWhenReceived: number;
     results: StatusCode[];
-    callback: (request: PublishRequest, response: PublishResponse) => void;
+    callback: (request: PublishRequest, response: PublishResponse | ServiceFault) => void;
 }
 
 function _assertValidPublishData(publishData: PublishData) {
@@ -137,7 +137,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
 
         const tmp = srcPublishEngine.detach_subscription(subscription);
         destPublishEngine.add_subscription(tmp);
-        
+
         subscription.resetLifeTimeCounter();
         if (sendInitialValues) {
             /*  A Boolean parameter with the following values:
@@ -327,11 +327,12 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
     public on_close_subscription(subscription: IClosedOrTransferredSubscription): void {
         doDebug && debugLog("ServerSidePublishEngine#on_close_subscription", subscription.id);
         if (subscription.hasPendingNotifications) {
-            doDebug && debugLog(
-                "ServerSidePublishEngine#on_close_subscription storing subscription",
-                subscription.id,
-                " to _closed_subscriptions because it has pending notification"
-            );
+            doDebug &&
+                debugLog(
+                    "ServerSidePublishEngine#on_close_subscription storing subscription",
+                    subscription.id,
+                    " to _closed_subscriptions because it has pending notification"
+                );
             this._closed_subscriptions.push(subscription);
         } else {
             doDebug && debugLog("ServerSidePublishEngine#on_close_subscription disposing subscription", subscription.id);
@@ -402,7 +403,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
      */
     public _on_PublishRequest(
         request: PublishRequest,
-        callback?: (request1: PublishRequest, response: PublishResponse) => void
+        callback?: (request1: PublishRequest, response: PublishResponse| ServiceFault) => void
     ): void {
         callback = callback || dummy_function;
         assert(typeof callback === "function");
@@ -483,9 +484,10 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
                                 s.timeToKeepAlive +
                                 " m?=" +
                                 s.hasUncollectedMonitoredItemNotifications +
-                                " " + 
+                                " " +
                                 SubscriptionState[s.state] +
-                                " " + s.messageSent + 
+                                " " +
+                                s.messageSent +
                                 "]"
                         )
                         .join(" \n")
@@ -508,7 +510,8 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
             }
             const starving_subscription = this._find_starving_subscription();
             if (starving_subscription) {
-                doDebug && debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId  = "), starving_subscription.id);
+                doDebug &&
+                    debugLog(chalk.bgWhite.red("feeding most late subscription subscriptionId  = "), starving_subscription.id);
                 starving_subscription.process_subscription();
             }
         });
@@ -537,10 +540,10 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
 
     private _send_error_for_request(publishData: PublishData, statusCode: StatusCode): void {
         _assertValidPublishData(publishData);
-        const publishResponse = new PublishResponse({
+        const response = new ServiceFault({
             responseHeader: { serviceResult: statusCode }
         });
-        this._send_response_for_request(publishData, publishResponse);
+        this._send_response_for_request(publishData, response);
     }
 
     private _cancelPendingPublishRequest(statusCode: StatusCode): void {
@@ -629,7 +632,7 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
         assert(this.pendingPublishRequestCount > 0);
         assert(response.subscriptionId !== 0xffffff);
         const publishData = this._publish_request_queue.shift()!;
-        this._send_response_for_request(publishData, response);
+        this._send_valid_response_for_request(publishData, response);
     }
 
     public _on_tick(): void {
@@ -654,15 +657,17 @@ export class ServerSidePublishEngine extends EventEmitter implements IServerSide
             this._send_error_for_request(publishData, StatusCodes.BadTimeout);
         }
     }
-
-    public _send_response_for_request(publishData: PublishData, response: PublishResponse): void {
+    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 {
         if (doDebug) {
             debugLog("_send_response_for_request ", response.toString());
         }
         _assertValidPublishData(publishData);
         // xx assert(response.responseHeader.requestHandle !== 0,"expecting a valid requestHandle");
         response.results = publishData.results;
-        response.responseHeader.requestHandle = publishData.request.requestHeader.requestHandle;
-        publishData.callback(publishData.request, response);
+        this._send_response_for_request(publishData, response);
     }
 }

programming_language: Java
filename: test_server_engine_subscription.js
change_type: ModificationType.MODIFY
diff: @@ -6,6 +6,7 @@ const { MonitoringMode, PublishRequest } = require("node-opcua-service-subscript
 const { StatusCodes, StatusCode } = require("node-opcua-status-code");
 const { TimestampsToReturn } = require("node-opcua-service-read");
 const { MonitoredItemCreateRequest } = require("node-opcua-service-subscription");
+const { ServiceFault } = require("node-opcua-types");
 
 const { get_mini_nodeset_filename } = require("node-opcua-address-space/testHelpers");
 
@@ -402,19 +403,22 @@ describe("ServerEngine Subscriptions service", function () {
             publishSpy.callCount.should.eql(4);
 
             publishSpy.getCall(1).args[1].responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
-            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(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].responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
-            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(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].responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
-            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);
+            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);
 
             await engine.closeSession(session.authenticationToken, true, "CloseSession");
         });

programming_language: JavaScript
filename: test_server_publish_engine.ts
change_type: ModificationType.MODIFY
diff: @@ -185,7 +185,7 @@ describe("Testing the server publish engine", function (this: any) {
         serverSidePublishEngine.dispose();
     });
 
-    it("ZDZ-5 a server should return BadNoSubscription as a response for a publish Request if there is no subscription available for this session. ", () => {
+    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. ", () => {
         // create a server - server has no subscription
         const publish_server = new ServerSidePublishEngine();
 
@@ -199,7 +199,7 @@ describe("Testing the server publish engine", function (this: any) {
         flushPending();
 
         send_response_for_request_spy.callCount.should.equal(1);
-        send_response_for_request_spy.getCall(0).args[1].schema.name.should.equal("PublishResponse");
+        send_response_for_request_spy.getCall(0).args[1].schema.name.should.equal("ServiceFault");
         send_response_for_request_spy.getCall(0).args[1].responseHeader.serviceResult.should.eql(StatusCodes.BadNoSubscription);
 
         publish_server.shutdown();
@@ -309,25 +309,23 @@ describe("Testing the server publish engine", function (this: any) {
         // xx console.log(send_response_for_request_spy.getCall(0).args[1].responseHeader.toString());
         // xx console.log(send_response_for_request_spy.getCall(1).args[1].responseHeader.toString());
 
-        send_response_for_request_spy.getCall(1).args[1].schema.name.should.equal("PublishResponse");
+        send_response_for_request_spy.getCall(1).args[1].schema.name.should.equal("ServiceFault");
         send_response_for_request_spy
             .getCall(1)
             .args[1].responseHeader.serviceResult.should.eql(StatusCodes.BadTooManyPublishRequests);
         send_response_for_request_spy.getCall(1).args[1].responseHeader.requestHandle.should.eql(2);
-        send_response_for_request_spy.getCall(1).args[1].results!.should.eql([]);
-
+       
         publish_server._on_PublishRequest(new PublishRequest({ requestHeader: { requestHandle: 8 } }));
         flushPending();
 
         send_response_for_request_spy.callCount.should.be.equal(3);
         // xx console.log(send_response_for_request_spy.getCall(2).args[1].responseHeader.toString());
-        send_response_for_request_spy.getCall(2).args[1].schema.name.should.equal("PublishResponse");
+        send_response_for_request_spy.getCall(2).args[1].schema.name.should.equal("ServiceFault");
         send_response_for_request_spy
             .getCall(2)
             .args[1].responseHeader.serviceResult.should.eql(StatusCodes.BadTooManyPublishRequests);
         send_response_for_request_spy.getCall(2).args[1].responseHeader.requestHandle.should.eql(3);
-        send_response_for_request_spy.getCall(2).args[1].results!.should.eql([]);
-
+    
         subscription.terminate();
         subscription.dispose();
         publish_server.shutdown();

programming_language: Java
filename: parallel_test.js
change_type: ModificationType.MODIFY
diff: @@ -302,6 +302,10 @@ if (isMainThread) {
                 if (key.name === "l") {
                     dumpRunningTests();
                 }
+                if (key.name === "i") {
+                    dumpRunningTests();
+                    dumpRunningTestsLogs();
+                }
             });
         }
 
@@ -315,14 +319,7 @@ if (isMainThread) {
         const infoTimer = setInterval(() => {
             console.log("----------------------------------------------- RUNNING TESTS ");
             dumpRunningTests();
-            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"));
-                }
-            }
+            dumpRunningTestsLogs();
         }, testWatchDogTimeout);
 
         fileMax = testFiles.length;
@@ -342,3 +339,13 @@ if (isMainThread) {
     const { workerThread } = require("./parallel_test_worker");
     workerThread();
 }
+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"));
+        }
+    }
+}

programming_language: TypeScript