CVE-2022-24375 (JavaScript, published 2022-08-24T05:15Z)
Description: value: The package node-opcua before 2.74.0 are vulnerable to Denial of Service (DoS) when bypassing the limitations for excessive memory consumption by sending multiple CloseSession requests with the deleteSubscription parameter equal to False.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-400 \u2014 Uncontrolled Resource Consumption
The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
Repository: https://github.com/node-opcua/node-opcua
Files changed:
packages/node-opcua-server/source/server_subscription.ts
packages/node-opcua-server/test/test_subscription.js
Commit message: server: fix Subscription.modify assert


Vulnerable code:
        this._start_timer();
        this._start_timer();
           this.emit("lifeTimeExpired");
    private _start_timer() {
        assert(this.messageSent === false);
        assert(this.state === SubscriptionState.CREATING);

Fixed code:
        this._start_timer({ firstTime: true });
        this._start_timer({ firstTime: false });
            this.emit("lifeTimeExpired");
    private _start_timer({ firstTime }: { firstTime: boolean }) {

        if (firstTime) {
            assert(this.messageSent === false);
            assert(this.state === SubscriptionState.CREATING);
        }
    it("T17 - should be possible to  modify subscription publishing interval", function(){
        // pretend the client has sent many pending PublishRequests
        fake_publish_engine.pendingPublishRequestCount = 1000;

        /**
         * When a Subscription is created, the first Message is sent at the end of the first publishing cycle to
         * inform the Client that the Subscription is operational. A Notification Message is sent if there are
         * Notifications ready to be reported. If there are none, a keep-alive Message is sent instead that
         * contains a sequence number of 1, indicating that the first Notification Message has not yet been
         * sent. This is the only time a keep-alive Message is sent without waiting for the maximum keep-alive
         * count to be reached, as specified in (f) above.
         *
         */
        const subscription = new Subscription({
            publishingInterval: 1000,
            maxKeepAliveCount: 20,
            //
            publishEngine: fake_publish_engine,
            globalCounter: { totalMonitoredItemCount: 0 },
            serverCapabilities: { maxMonitoredItems: 10000, maxMonitoredItemsPerSubscription: 1000 }
        });
        const monitoredItem = add_mock_monitored_item(subscription);

        // pretend that we already have notification messages
        // a notification finally arrived !
        monitoredItem.simulateMonitoredItemAddingNotification();

        const notification_event_spy = sinon.spy();
        const keepalive_event_spy = sinon.spy();
        const expire_event_spy = sinon.spy();

        subscription.on("notification", notification_event_spy);
        subscription.on("keepalive", keepalive_event_spy);
        subscription.on("expired", expire_event_spy);

        test.clock.tick(200);
        keepalive_event_spy.callCount.should.equal(0);
        notification_event_spy.callCount.should.eql(0);

        test.clock.tick(1000);
        keepalive_event_spy.callCount.should.equal(0);
        notification_event_spy.callCount.should.eql(1);

        test.clock.tick(1000);
        keepalive_event_spy.callCount.should.equal(0);
        notification_event_spy.callCount.should.eql(1);

        monitoredItem.simulateMonitoredItemAddingNotification();
        // now change the publishin Intervale
        subscription.modify({ requestedPublishingInterval: 2000});
       
        test.clock.tick(1000);
        keepalive_event_spy.callCount.should.equal(0);
        notification_event_spy.callCount.should.eql(1);

        test.clock.tick(1010);
        keepalive_event_spy.callCount.should.equal(0);
        notification_event_spy.callCount.should.eql(2);

        subscription.terminate();
        subscription.dispose();

    });