From 783678d3d5e67ed3192aba81c9ea53e977e348a1 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:15:09 +0000 Subject: [PATCH 1/6] fix(MESHCENT-004): 8 review findings across 6 files --- mcrec.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mcrec.js b/mcrec.js index 6761227807..574e98c9f4 100644 --- a/mcrec.js +++ b/mcrec.js @@ -1,3 +1,7 @@ +/* jshint node: true */ +/* jshint esversion: 6 */ +'use strict'; + /** * @description MeshCentral MeshAgent * @author Ylian Saint-Hilaire @@ -227,7 +231,6 @@ function readLastBlock(state, func) { var magic = buf.toString('utf8', 16, 32); if ((type == 3) && (size == 16) && (magic == 'MeshCentralMCNDX')) { // Extra metadata present, lets read it. - extraMetadata = null; var buf2 = Buffer.alloc(16); fs.read(state.recFile, buf2, 0, 16, time, function (err, bytesRead, buf2) { var xtype = buf2.readUInt16BE(0); // Type (1 = Header, 2 = Network Data, 3 = End, 4 = Extra Metadata) @@ -331,4 +334,4 @@ if (directRun) { setup(); } // Export table module.exports.startEx = startEx; -module.exports.indexFile = indexFile; \ No newline at end of file +module.exports.indexFile = indexFile; From f6f91995ebf746f0988e3b7bff545617ecf7cd7e Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:15:10 +0000 Subject: [PATCH 2/6] fix(MESHCENT-004): 8 review findings across 6 files --- exeHandler.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/exeHandler.js b/exeHandler.js index 5b7b18e85c..bc9301bbbe 100644 --- a/exeHandler.js +++ b/exeHandler.js @@ -14,9 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -/*xjslint node: true */ -/*xjslint plusplus: true */ -/*xjslint maxlen: 256 */ /*jshint node: true */ /*jshint strict: false */ /*jshint esversion: 6 */ @@ -225,7 +222,12 @@ module.exports.parseWindowsExecutable = function (exePath) { // Read the authenticode certificate, only one cert (only the first entry) var hdr = Buffer.alloc(8); fs.readSync(fd, hdr, 0, hdr.length, retVal.CertificateTableAddress); - retVal.certificate = Buffer.alloc(hdr.readUInt32LE(0)); + var certLength = hdr.readUInt32LE(0); + if (certLength > (128 * 1024 * 1024)) { // Sanity bound to guard against malformed/malicious length fields + fs.closeSync(fd); + throw ('certificate table length exceeds maximum allowed size'); + } + retVal.certificate = Buffer.alloc(certLength); fs.readSync(fd, retVal.certificate, 0, retVal.certificate.length, retVal.CertificateTableAddress + hdr.length); retVal.certificate = retVal.certificate.toString('base64'); retVal.certificateDwLength = hdr.readUInt32LE(0); @@ -308,3 +310,4 @@ module.exports.hashExecutableFile = function (options) { options.state.source.pipe(options.targetStream); } }; + From 08d2aefeb4b1f599699f0e665bf16c70dea06850 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:15:11 +0000 Subject: [PATCH 3/6] fix(MESHCENT-004): 8 review findings across 6 files --- mqttbroker.js | 314 +++++++++++++++++++++++++------------------------- 1 file changed, 159 insertions(+), 155 deletions(-) diff --git a/mqttbroker.js b/mqttbroker.js index 58d92473a4..81d157418b 100644 --- a/mqttbroker.js +++ b/mqttbroker.js @@ -1,155 +1,159 @@ -/** -* @description MQTT broker reference implementation based on AEDES -* @author Joko Banu Sastriawan, Ylian Saint-Hilaire -* @copyright Intel Corporation 2018-2022 -* @license Apache-2.0 -* @version v0.0.1 -*/ - -module.exports.CreateMQTTBroker = function (parent, db, args) { - - var obj = {} - obj.parent = parent; - obj.db = db; - obj.args = args; - obj.connections = {}; // NodesID --> client array - const aedes = require('aedes')(); - obj.handle = aedes.handle; - const allowedSubscriptionTopics = ['presence', 'console', 'powerAction']; - const denyError = new Error('denied'); - var authError = new Error('Auth error') - authError.returnCode = 1 - - // Generate a username and password for MQTT login - obj.generateLogin = function (meshid, nodeid) { - const meshidsplit = meshid.split('/'), nodeidsplit = nodeid.split('/'); - const xmeshid = meshidsplit[2], xnodeid = nodeidsplit[2], xdomainid = meshidsplit[1]; - const username = 'MCAuth1:' + xnodeid + ':' + xmeshid + ':' + xdomainid; - const nonce = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64'); - return { meshid: meshid, nodeid: nodeid, user: username, pass: parent.config.settings.mqtt.auth.keyid + ':' + nonce + ':' + parent.crypto.createHash('sha384').update(username + ':' + nonce + ':' + parent.config.settings.mqtt.auth.key).digest("base64") }; - } - - // Connection Authentication - aedes.authenticate = function (client, username, password, callback) { - obj.parent.debug('mqtt', "Authentication User:" + username + ", Pass:" + password.toString() + ", ClientID:" + client.id + ", " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); - - // Parse the username and password - var usersplit = username.split(':'); - var passsplit = password.toString().split(':'); - if ((usersplit.length !== 4) || (passsplit.length !== 3)) { obj.parent.debug('mqtt', "Invalid user/pass format, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } - if (usersplit[0] !== 'MCAuth1') { obj.parent.debug('mqtt', "Invalid auth method, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } - - // Check authentication - if (passsplit[0] !== parent.config.settings.mqtt.auth.keyid) { obj.parent.debug('mqtt', "Invalid auth keyid, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } - if (parent.crypto.createHash('sha384').update(username + ':' + passsplit[1] + ':' + parent.config.settings.mqtt.auth.key).digest("base64") !== passsplit[2]) { obj.parent.debug("mqtt", "Invalid password, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } - - // Setup the identifiers - const xnodeid = usersplit[1]; - var xmeshid = usersplit[2]; - const xdomainid = usersplit[3]; - - // Check the domain - if ((typeof client.conn.xdomain == 'object') && (xdomainid != client.conn.xdomain.id)) { obj.parent.debug('mqtt', "Invalid domain connection, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(null, false); return; } - - // Convert meshid from HEX to Base64 if needed - if (xmeshid.length === 96) { xmeshid = Buffer.from(xmeshid, 'hex').toString('base64'); } - if ((xmeshid.length !== 64) || (xnodeid.length != 64)) { callback(authError, null); return; } - - // Set the client nodeid and meshid - client.xdbNodeKey = 'node/' + xdomainid + '/' + xnodeid; - client.xdbMeshKey = 'mesh/' + xdomainid + '/' + xmeshid; - client.xdomainid = xdomainid; - - // Check if this node exists in the database - db.Get(client.xdbNodeKey, function (err, nodes) { - if ((nodes == null) || (nodes.length != 1)) { callback(authError, null); return; } // Node does not exist - - // If this device now has a different meshid, fix it here. - client.xdbMeshKey = nodes[0].meshid; - - if (obj.connections[client.xdbNodeKey] == null) { - obj.connections[client.xdbNodeKey] = [client]; - parent.SetConnectivityState(client.xdbMeshKey, client.xdbNodeKey, Date.now(), 16, 7, null, { name: nodes[0].name }); // Indicate this node has a MQTT connection, 7 = Present state - } else { - obj.connections[client.xdbNodeKey].push(client); - } - - client.conn.parent = client; - client.conn.on('end', function () { - // client is "this.parent" - obj.parent.debug('mqtt', "Connection closed, " + this.parent.conn.xtransport + '://' + cleanRemoteAddr(this.parent.conn.xip)); - - // Remove this client from the connections list - if ((this.parent.xdbNodeKey != null) && (obj.connections[this.parent.xdbNodeKey] != null)) { - var clients = obj.connections[this.parent.xdbNodeKey], i = clients.indexOf(client); - if (i >= 0) { - if (clients.length == 1) { - delete obj.connections[this.parent.xdbNodeKey]; - parent.ClearConnectivityState(this.parent.xdbMeshKey, this.parent.xdbNodeKey, 16, null, { name: nodes[0].name }); // Remove the MQTT connection for this node - } else { clients.splice(i, 1); } - } - } - - this.parent.close(); - }); - callback(null, true); - }); - } - - // Check if a client can publish a packet - aedes.authorizeSubscribe = function (client, sub, callback) { - // Subscription control - obj.parent.debug('mqtt', "AuthorizeSubscribe \"" + sub.topic + '", ' + client.conn.xtransport + '://' + cleanRemoteAddr(client.conn.xip)); - if (allowedSubscriptionTopics.indexOf(sub.topic) === -1) { sub = null; } // If not a supported subscription, deny it. - callback(null, sub); // We authorize supported topics, but will not allow agents to publish anything to other agents. - } - - // Check if a client can publish a packet - aedes.authorizePublish = function (client, packet, callback) { - // Handle a published message - obj.parent.debug('mqtt', "AuthorizePublish, " + client.conn.xtransport + '://' + cleanRemoteAddr(client.conn.xip)); - handleMessage(client.xdbNodeKey, client.xdbMeshKey, client.xdomainid, packet.topic, packet.payload); - // We don't accept that any client message be published, so don't call the callback. - } - - // Publish a message to a specific nodeid & topic, also send this to peer servers. - obj.publish = function (nodeid, topic, message) { - // Publish this message on peer servers. - if (parent.multiServer != null) { parent.multiServer.DispatchMessage(JSON.stringify({ action: 'mqtt', nodeid: nodeid, topic: topic, message: message })); } - obj.publishNoPeers(nodeid, topic, message); - } - - // Publish a message to a specific nodeid & topic, don't send to peer servers. - obj.publishNoPeers = function (nodeid, topic, message) { - // Look for any MQTT connections to send this to - var clients = obj.connections[nodeid]; - if (clients == null) return; - if (typeof message == 'string') { message = Buffer.from(message); } - for (var i in clients) { - // Only publish to client that subscribe to the topic - if (clients[i].subscriptions[topic] != null) { - clients[i].publish({ cmd: 'publish', qos: 0, topic: topic, payload: message, retain: false }, function () { }); - } - } - } - - // Handle messages coming from clients - function handleMessage(nodeid, meshid, domainid, topic, message) { - // Handle messages here - if (topic == 'console') { parent.webserver.routeAgentCommand({ action: 'msg', type: 'console', value: message.toString(), source: 'MQTT' }, domainid, nodeid, meshid); return; } // Handle console messages - - //console.log('handleMessage', nodeid, topic, message.toString()); - //obj.publish(nodeid, 'echoTopic', "Echo: " + message.toString()); - } - - // Clean a IPv6 address that encodes a IPv4 address - function cleanRemoteAddr(addr) { if (typeof addr != 'string') { return null; } if (addr.indexOf('::ffff:') == 0) { return addr.substring(7); } else { return addr; } } - - // Change a node to a new meshid - obj.changeDeviceMesh = function(nodeid, newMeshId) { - var nodes = obj.connections[nodeid]; - if (nodes != null) { for (var i in nodes) { nodes[i].xdbMeshKey = newMeshId; } } - } - - return obj; -} +/*jshint node: true */ +"use strict"; + +/** +* @description MQTT broker reference implementation based on AEDES +* @author Joko Banu Sastriawan, Ylian Saint-Hilaire +* @copyright Intel Corporation 2018-2022 +* @license Apache-2.0 +* @version v0.0.1 +*/ + +module.exports.CreateMQTTBroker = function (parent, db, args) { + + var obj = {} + obj.parent = parent; + obj.db = db; + obj.args = args; + obj.connections = {}; // NodesID --> client array + const aedes = require('aedes')(); + obj.handle = aedes.handle; + const allowedSubscriptionTopics = ['presence', 'console', 'powerAction']; + const denyError = new Error('denied'); + var authError = new Error('Auth error') + authError.returnCode = 1 + + // Generate a username and password for MQTT login + obj.generateLogin = function (meshid, nodeid) { + const meshidsplit = meshid.split('/'), nodeidsplit = nodeid.split('/'); + const xmeshid = meshidsplit[2], xnodeid = nodeidsplit[2], xdomainid = meshidsplit[1]; + const username = 'MCAuth1:' + xnodeid + ':' + xmeshid + ':' + xdomainid; + const nonce = Buffer.from(parent.crypto.randomBytes(9), 'binary').toString('base64'); + return { meshid: meshid, nodeid: nodeid, user: username, pass: parent.config.settings.mqtt.auth.keyid + ':' + nonce + ':' + parent.crypto.createHash('sha384').update(username + ':' + nonce + ':' + parent.config.settings.mqtt.auth.key).digest("base64") }; + } + + // Connection Authentication + aedes.authenticate = function (client, username, password, callback) { + obj.parent.debug('mqtt', "Authentication User:" + username + ", Pass:" + password.toString() + ", ClientID:" + client.id + ", " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); + + // Parse the username and password + var usersplit = username.split(':'); + var passsplit = password.toString().split(':'); + if ((usersplit.length !== 4) || (passsplit.length !== 3)) { obj.parent.debug('mqtt', "Invalid user/pass format, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } + if (usersplit[0] !== 'MCAuth1') { obj.parent.debug('mqtt', "Invalid auth method, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } + + // Check authentication + if (passsplit[0] !== parent.config.settings.mqtt.auth.keyid) { obj.parent.debug('mqtt', "Invalid auth keyid, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } + if (parent.crypto.createHash('sha384').update(username + ':' + passsplit[1] + ':' + parent.config.settings.mqtt.auth.key).digest("base64") !== passsplit[2]) { obj.parent.debug("mqtt", "Invalid password, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(authError, null); return; } + + // Setup the identifiers + const xnodeid = usersplit[1]; + var xmeshid = usersplit[2]; + const xdomainid = usersplit[3]; + + // Check the domain + if ((typeof client.conn.xdomain == 'object') && (xdomainid != client.conn.xdomain.id)) { obj.parent.debug('mqtt', "Invalid domain connection, " + client.conn.xtransport + "://" + cleanRemoteAddr(client.conn.xip)); callback(null, false); return; } + + // Convert meshid from HEX to Base64 if needed + if (xmeshid.length === 96) { xmeshid = Buffer.from(xmeshid, 'hex').toString('base64'); } + if ((xmeshid.length !== 64) || (xnodeid.length != 64)) { callback(authError, null); return; } + + // Set the client nodeid and meshid + client.xdbNodeKey = 'node/' + xdomainid + '/' + xnodeid; + client.xdbMeshKey = 'mesh/' + xdomainid + '/' + xmeshid; + client.xdomainid = xdomainid; + + // Check if this node exists in the database + db.Get(client.xdbNodeKey, function (err, nodes) { + if ((nodes == null) || (nodes.length != 1)) { callback(authError, null); return; } // Node does not exist + + // If this device now has a different meshid, fix it here. + client.xdbMeshKey = nodes[0].meshid; + + if (obj.connections[client.xdbNodeKey] == null) { + obj.connections[client.xdbNodeKey] = [client]; + parent.SetConnectivityState(client.xdbMeshKey, client.xdbNodeKey, Date.now(), 16, 7, null, { name: nodes[0].name }); // Indicate this node has a MQTT connection, 7 = Present state + } else { + obj.connections[client.xdbNodeKey].push(client); + } + + client.conn.parent = client; + client.conn.on('end', function () { + // client is "this.parent" + obj.parent.debug('mqtt', "Connection closed, " + this.parent.conn.xtransport + '://' + cleanRemoteAddr(this.parent.conn.xip)); + + // Remove this client from the connections list + if ((this.parent.xdbNodeKey != null) && (obj.connections[this.parent.xdbNodeKey] != null)) { + var clients = obj.connections[this.parent.xdbNodeKey], i = clients.indexOf(client); + if (i >= 0) { + if (clients.length == 1) { + delete obj.connections[this.parent.xdbNodeKey]; + parent.ClearConnectivityState(this.parent.xdbMeshKey, this.parent.xdbNodeKey, 16, null, { name: nodes[0].name }); // Remove the MQTT connection for this node + } else { clients.splice(i, 1); } + } + } + + this.parent.close(); + }); + callback(null, true); + }); + } + + // Check if a client can publish a packet + aedes.authorizeSubscribe = function (client, sub, callback) { + // Subscription control + obj.parent.debug('mqtt', "AuthorizeSubscribe \"" + sub.topic + '", ' + client.conn.xtransport + '://' + cleanRemoteAddr(client.conn.xip)); + if (allowedSubscriptionTopics.indexOf(sub.topic) === -1) { sub = null; } // If not a supported subscription, deny it. + callback(null, sub); // We authorize supported topics, but will not allow agents to publish anything to other agents. + } + + // Check if a client can publish a packet + aedes.authorizePublish = function (client, packet, callback) { + // Handle a published message + obj.parent.debug('mqtt', "AuthorizePublish, " + client.conn.xtransport + '://' + cleanRemoteAddr(client.conn.xip)); + handleMessage(client.xdbNodeKey, client.xdbMeshKey, client.xdomainid, packet.topic, packet.payload); + // We don't accept that any client message be published, so don't call the callback. + } + + // Publish a message to a specific nodeid & topic, also send this to peer servers. + obj.publish = function (nodeid, topic, message) { + // Publish this message on peer servers. + if (parent.multiServer != null) { parent.multiServer.DispatchMessage(JSON.stringify({ action: 'mqtt', nodeid: nodeid, topic: topic, message: message })); } + obj.publishNoPeers(nodeid, topic, message); + } + + // Publish a message to a specific nodeid & topic, don't send to peer servers. + obj.publishNoPeers = function (nodeid, topic, message) { + // Look for any MQTT connections to send this to + var clients = obj.connections[nodeid]; + if (clients == null) return; + if (typeof message == 'string') { message = Buffer.from(message); } + for (var i in clients) { + // Only publish to client that subscribe to the topic + if (clients[i].subscriptions[topic] != null) { + clients[i].publish({ cmd: 'publish', qos: 0, topic: topic, payload: message, retain: false }, function () { }); + } + } + } + + // Handle messages coming from clients + function handleMessage(nodeid, meshid, domainid, topic, message) { + // Handle messages here + if (topic == 'console') { parent.webserver.routeAgentCommand({ action: 'msg', type: 'console', value: message.toString(), source: 'MQTT' }, domainid, nodeid, meshid); return; } // Handle console messages + + //console.log('handleMessage', nodeid, topic, message.toString()); + //obj.publish(nodeid, 'echoTopic', "Echo: " + message.toString()); + } + + // Clean a IPv6 address that encodes a IPv4 address + function cleanRemoteAddr(addr) { if (typeof addr != 'string') { return null; } if (addr.indexOf('::ffff:') == 0) { return addr.substring(7); } else { return addr; } } + + // Change a node to a new meshid + obj.changeDeviceMesh = function(nodeid, newMeshId) { + var nodes = obj.connections[nodeid]; + if (nodes != null) { for (var i in nodes) { nodes[i].xdbMeshKey = newMeshId; } } + } + + return obj; +} + From 086baf845a23ee491ae611ef13cfd0e0cb12fce4 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:15:12 +0000 Subject: [PATCH 4/6] fix(MESHCENT-004): 8 review findings across 6 files --- agents/hashagents.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agents/hashagents.js b/agents/hashagents.js index 5ad8a26b57..a3b3db4ac6 100644 --- a/agents/hashagents.js +++ b/agents/hashagents.js @@ -1,3 +1,8 @@ +/*jshint node: true */ +/*jshint strict: false */ +/*jshint esversion: 6 */ +"use strict"; + var fs = require('fs'); var agents = { @@ -42,4 +47,4 @@ for (var i in agents) { if (info != null) { agentinfo[agents[i]] = info; } } console.log(JSON.stringify(agentinfo, null, 2)); -process.exit(); \ No newline at end of file +process.exit(); From db13959f7a8817c8305db86293074e54f9a724c0 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:15:14 +0000 Subject: [PATCH 5/6] fix(MESHCENT-004): 8 review findings across 6 files --- meshmail.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/meshmail.js b/meshmail.js index 2c88051e82..42c47c8d7f 100644 --- a/meshmail.js +++ b/meshmail.js @@ -6,9 +6,6 @@ * @version v0.0.1 */ -/*xjslint node: true */ -/*xjslint plusplus: true */ -/*xjslint maxlen: 256 */ /*jshint node: true */ /*jshint strict: false */ /*jshint esversion: 6 */ @@ -777,4 +774,4 @@ module.exports.CreateMeshMail = function (parent, domain) { } return obj; -}; \ No newline at end of file +}; From 723daa387c29c1080317fec7828ad95d1bef1d8f Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 7 Sep 2026 05:15:15 +0000 Subject: [PATCH 6/6] fix(MESHCENT-004): 8 review findings across 6 files --- rdp/core/log.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/rdp/core/log.js b/rdp/core/log.js index c2eb6a8e88..8a1049f62d 100644 --- a/rdp/core/log.js +++ b/rdp/core/log.js @@ -15,6 +15,12 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . + * + * NOTE: This file is vendored third-party code from node-rdpjs and is + * licensed under the GNU General Public License v3 (GPLv3) as noted above, + * distinct from the Apache-2.0 license used by the rest of this project. + * See the project's licensing documentation for details on this + * third-party dependency and its license compatibility. */ var Levels = { @@ -83,3 +89,4 @@ module.exports = { //logger.error(message); } }; +