Version 2.2.0

Update dependencies (security fix)
Fix lint errors
This commit is contained in:
nioc 2022-09-28 01:18:01 +02:00
parent e7f8967176
commit 061725526b
11 changed files with 9952 additions and 4692 deletions

View file

@ -15,7 +15,7 @@ module.exports = function Configuration (logger, configPath = null) {
configPath = './lib/config/config.json' configPath = './lib/config/config.json'
} }
try { try {
let data = require('fs').readFileSync(configPath) const data = require('fs').readFileSync(configPath)
config = JSON.parse(data) config = JSON.parse(data)
} catch (error) { } catch (error) {
logger.fatal(`Invalid configuration file: ${error.message}, current directory is: ${process.cwd()}`) logger.fatal(`Invalid configuration file: ${error.message}, current directory is: ${process.cwd()}`)

View file

@ -10,7 +10,7 @@
*/ */
module.exports = (logger, xmpp) => { module.exports = (logger, xmpp) => {
let nodeCleanup = require('node-cleanup') const nodeCleanup = require('node-cleanup')
nodeCleanup(function (exitCode, signal) { nodeCleanup(function (exitCode, signal) {
logger.warn(`Received ${exitCode}/${signal} (application is closing), disconnect from XMPP server`) logger.warn(`Received ${exitCode}/${signal} (application is closing), disconnect from XMPP server`)
try { try {

View file

@ -25,7 +25,7 @@ module.exports = () => {
} }
// add appenders // add appenders
let appenders = [] const appenders = []
if (config.file.active) { if (config.file.active) {
const fs = require('fs') const fs = require('fs')
if (!fs.existsSync(config.file.path)) { if (!fs.existsSync(config.file.path)) {
@ -73,7 +73,7 @@ module.exports = () => {
} }
}, },
categories: { categories: {
default: { appenders: appenders, level: 'info' } default: { appenders, level: 'info' }
} }
}) })
logger = log4js.getLogger() logger = log4js.getLogger()

View file

@ -10,7 +10,7 @@
*/ */
module.exports = async (logger, config, xmpp, user, destination, message, type, code) => { module.exports = async (logger, config, xmpp, user, destination, message, type, code) => {
let webhook = config.getOutgoingWebhook(code) const webhook = config.getOutgoingWebhook(code)
if (!webhook) { if (!webhook) {
logger.warn(`There is no webhook with code "${code}"`) logger.warn(`There is no webhook with code "${code}"`)
throw new Error(`There is no webhook with code "${code}"`) throw new Error(`There is no webhook with code "${code}"`)
@ -18,7 +18,7 @@ module.exports = async (logger, config, xmpp, user, destination, message, type,
const { promisify } = require('util') const { promisify } = require('util')
const request = promisify(require('request')) const request = promisify(require('request'))
// request.debug = true // request.debug = true
let options = { const options = {
method: 'POST', method: 'POST',
url: webhook.url, url: webhook.url,
strictSSL: webhook.strictSSL strictSSL: webhook.strictSSL
@ -50,7 +50,7 @@ module.exports = async (logger, config, xmpp, user, destination, message, type,
logger.trace('Content-type: application/x-www-form-urlencoded') logger.trace('Content-type: application/x-www-form-urlencoded')
options.form = { options.form = {
from: user, from: user,
message: message, message,
channel: destination channel: destination
} }
logger.trace('Outgoing webhook request:', options.form) logger.trace('Outgoing webhook request:', options.form)
@ -59,7 +59,7 @@ module.exports = async (logger, config, xmpp, user, destination, message, type,
logger.trace('Content-type: application/json') logger.trace('Content-type: application/json')
options.json = { options.json = {
from: user, from: user,
message: message, message,
channel: destination channel: destination
} }
logger.trace('Outgoing webhook request:', options.json) logger.trace('Outgoing webhook request:', options.json)

View file

@ -1,10 +1,10 @@
'use strict' 'use strict'
// create default logger // create default logger
let logger = require('./logger')() const logger = require('./logger')()
// get configuration // get configuration
let config = require('./config')(logger) const config = require('./config')(logger)
// update logger with configuration // update logger with configuration
logger.updateConfig(config.logger) logger.updateConfig(config.logger)

View file

@ -18,7 +18,7 @@ module.exports = (logger, config, xmpp) => {
const port = config.listener.port || 8000 const port = config.listener.port || 8000
const portSsl = config.listener.ssl.port || 8001 const portSsl = config.listener.ssl.port || 8001
var webhook = express() const webhook = express()
// handle connection from proxy (get IP in 'X-Forwarded-For' header) // handle connection from proxy (get IP in 'X-Forwarded-For' header)
webhook.set('trust proxy', true) webhook.set('trust proxy', true)
@ -57,7 +57,7 @@ module.exports = (logger, config, xmpp) => {
// handle post request // handle post request
webhook.post(config.listener.path + '/*', (req, res) => { webhook.post(config.listener.path + '/*', (req, res) => {
logger.info(`Incoming webhook from ${req.auth.user}`) logger.info(`Incoming webhook from ${req.auth.user}`)
let webhook = config.getWebhookAction(req.path) const webhook = config.getWebhookAction(req.path)
if (!webhook) { if (!webhook) {
logger.error(`Webhook received: ${req.path}, not found`) logger.error(`Webhook received: ${req.path}, not found`)
return res.status(404).send('Webhook not found') return res.status(404).send('Webhook not found')
@ -65,21 +65,20 @@ module.exports = (logger, config, xmpp) => {
logger.debug(`Webhook received: ${webhook.path}, start action: ${webhook.action}`) logger.debug(`Webhook received: ${webhook.path}, start action: ${webhook.action}`)
logger.trace(req.body) logger.trace(req.body)
switch (webhook.action) { switch (webhook.action) {
case 'send_xmpp_message': case 'send_xmpp_message': {
// get destination // get destination
if ('destination' in req.body === false) { if ('destination' in req.body === false) {
logger.error('Destination not found') logger.error('Destination not found')
return res.status(400).send('Destination not found') return res.status(400).send('Destination not found')
} }
let destination = req.body.destination const destination = req.body.destination
// get message // get message
if ('message' in req.body === false) { if ('message' in req.body === false) {
logger.error('Message not found') logger.error('Message not found')
return res.status(400).send('Message not found') return res.status(400).send('Message not found')
} }
let message = req.body.message const message = req.body.message
// check if destination is a group chat // check if destination is a group chat
const type = config.xmpp.rooms.some((room) => room.id === destination) ? 'groupchat' : 'chat' const type = config.xmpp.rooms.some((room) => room.id === destination) ? 'groupchat' : 'chat'
@ -88,17 +87,17 @@ module.exports = (logger, config, xmpp) => {
logger.trace(`Send to ${destination} (group:${type}) following message :\r\n${message}`) logger.trace(`Send to ${destination} (group:${type}) following message :\r\n${message}`)
xmpp.send(destination, message, type) xmpp.send(destination, message, type)
.then(() => { .then(() => {
return res.status(200).send({ 'status': 'ok', destination }) return res.status(200).send({ status: 'ok', destination })
}) })
.catch(() => { .catch(() => {
return res.status(500).send('Could not send message') return res.status(500).send('Could not send message')
}) })
break break
}
case 'send_xmpp_template': case 'send_xmpp_template': {
// bind data in template // bind data in template
let msg = webhook.template.replace(/\$\{(.+?)\}/g, (match, $1) => { const msg = webhook.template.replace(/\$\{(.+?)\}/g, (match, $1) => {
return jmespath.search(req.body, $1) || '' return jmespath.search(req.body, $1) || ''
}) })
logger.trace(`Message:\r\n${msg}`) logger.trace(`Message:\r\n${msg}`)
@ -114,6 +113,7 @@ module.exports = (logger, config, xmpp) => {
return res.status(500).send('Could not send message') return res.status(500).send('Could not send message')
}) })
break break
}
default: default:
return res.status(204).send() return res.status(204).send()
@ -133,10 +133,10 @@ module.exports = (logger, config, xmpp) => {
// get IP v4 addresses and prepare endpoints for output // get IP v4 addresses and prepare endpoints for output
let addresses = [] let addresses = []
const networkInterfaces = require('os').networkInterfaces() const networkInterfaces = require('os').networkInterfaces()
for (let ifaceName in networkInterfaces) { for (const ifaceName in networkInterfaces) {
addresses = addresses.concat(networkInterfaces[ifaceName].reduce((add, iface) => { addresses = addresses.concat(networkInterfaces[ifaceName].reduce((add, iface) => {
if (iface['family'] === 'IPv4') { if (iface.family === 'IPv4') {
add.push(iface['address']) add.push(iface.address)
} }
return add return add
}, [])) }, []))
@ -166,7 +166,7 @@ module.exports = (logger, config, xmpp) => {
try { try {
fs.accessSync(config.listener.ssl.certPath, fs.constants.R_OK) fs.accessSync(config.listener.ssl.certPath, fs.constants.R_OK)
logger.debug('Can read certificate') logger.debug('Can read certificate')
let credentials = { const credentials = {
key: fs.readFileSync(config.listener.ssl.keyPath), key: fs.readFileSync(config.listener.ssl.keyPath),
cert: fs.readFileSync(config.listener.ssl.certPath) cert: fs.readFileSync(config.listener.ssl.certPath)
} }

View file

@ -55,7 +55,7 @@ module.exports = (logger, config) => {
}) })
// join rooms // join rooms
config.xmpp.rooms.forEach(function (room) { config.xmpp.rooms.forEach(function (room) {
let occupantJid = room.id + '/' + address.local const occupantJid = room.id + '/' + address.local
logger.debug(`Join room: ${room.id} ('${occupantJid}')`) logger.debug(`Join room: ${room.id} ('${occupantJid}')`)
const stanza = xml( const stanza = xml(
'presence', { 'presence', {
@ -78,16 +78,16 @@ module.exports = (logger, config) => {
// not a message, do nothing // not a message, do nothing
return return
} }
let type = stanza.attrs.type const type = stanza.attrs.type
switch (type) { switch (type) {
case 'chat': case 'chat':
case 'groupchat': case 'groupchat': {
let body = stanza.getChild('body') const body = stanza.getChild('body')
if (!body) { if (!body) {
// empty body, do nothing // empty body, do nothing
return return
} }
let fromJid = jid(stanza.attrs.from) const fromJid = jid(stanza.attrs.from)
// for chat, "to" and "replyTo" must be something like "user@domain.ltd", "from" is local part "user" // for chat, "to" and "replyTo" must be something like "user@domain.ltd", "from" is local part "user"
let to = this.jid.bare() let to = this.jid.bare()
let from = fromJid.local let from = fromJid.local
@ -102,15 +102,15 @@ module.exports = (logger, config) => {
return return
} }
} }
let message = body.text() const message = body.text()
// handle message delivery receipts for chat // handle message delivery receipts for chat
if (type === 'chat') { if (type === 'chat') {
let request = stanza.getChild('request') const request = stanza.getChild('request')
if (request && if (request &&
request.attrs.xmlns && request.attrs.xmlns &&
request.attrs.xmlns === 'urn:xmpp:receipts' && request.attrs.xmlns === 'urn:xmpp:receipts' &&
stanza.attrs.id) { stanza.attrs.id) {
logger.debug(`Message delivery receipt is requested and will be processed`) logger.debug('Message delivery receipt is requested and will be processed')
const receiptStanza = xml( const receiptStanza = xml(
'message', { 'message', {
to: fromJid to: fromJid
@ -127,7 +127,7 @@ module.exports = (logger, config) => {
} }
logger.info(`Incoming ${type} message from ${from} (${fromJid.toString()}) to ${to}`) logger.info(`Incoming ${type} message from ${from} (${fromJid.toString()}) to ${to}`)
logger.debug(`Message: "${message.replace(/\n|\r/g, ' ')}"`) logger.debug(`Message: "${message.replace(/\n|\r/g, ' ')}"`)
let xmppHook = config.getXmppHookAction(to.toString()) const xmppHook = config.getXmppHookAction(to.toString())
if (!xmppHook) { if (!xmppHook) {
logger.error(`There is no action for incoming ${type} message to: "${to}"`) logger.error(`There is no action for incoming ${type} message to: "${to}"`)
return return
@ -144,6 +144,7 @@ module.exports = (logger, config) => {
break break
} }
break break
}
} }
}) })

14523
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "xmpp-bot", "name": "xmpp-bot",
"version": "2.1.0", "version": "2.2.0",
"description": "XMPP bot", "description": "XMPP bot",
"main": "./lib/server.js", "main": "./lib/server.js",
"scripts": { "scripts": {
@ -12,7 +12,7 @@
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls" "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls"
}, },
"engines": { "engines": {
"node": ">= 10.0.0" "node": ">= 12.4.0"
}, },
"author": "nioc <dev@nioc.eu>", "author": "nioc <dev@nioc.eu>",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
@ -22,30 +22,29 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@xmpp/client": "^0.8.0", "@xmpp/client": "^0.13.1",
"body-parser": "^1.19.0", "body-parser": "^1.20.0",
"express": "^4.17.1", "express": "^4.18.1",
"express-basic-auth": "^1.2.0", "express-basic-auth": "^1.2.1",
"jmespath": "^0.15.0", "jmespath": "^0.16.0",
"log4js": "^4.5.1", "log4js": "^6.6.1",
"morgan": "^1.9.1", "morgan": "^1.10.0",
"node-cleanup": "^2.1.2", "node-cleanup": "^2.1.2",
"request": "^2.88.0" "request": "^2.88.2"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.2.0", "chai": "^4.3.6",
"coveralls": "^3.0.8", "coveralls": "^3.0.9",
"eslint": "^6.7.0", "eslint": "^8.24.0",
"eslint-config-standard": "^12.0.0", "eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.18.2", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^9.2.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.2.1", "eslint-plugin-promise": "^6.0.1",
"eslint-plugin-standard": "^4.0.1", "mocha": "^10.0.0",
"mocha": "^6.2.2",
"mock-require": "^3.0.3", "mock-require": "^3.0.3",
"nock": "^11.7.0", "nock": "^13.2.9",
"nodemon": "^1.19.4", "nodemon": "^2.0.20",
"nyc": "^14.1.1", "nyc": "^15.1.0",
"sinon": "^7.5.0" "sinon": "^14.0.0"
} }
} }

View file

@ -1,5 +1,6 @@
'use strict' 'use strict'
/* eslint-disable handle-callback-err */ /* eslint-disable n/handle-callback-err */
/* eslint-disable prefer-regex-literals */
process.env.NODE_ENV = 'production' process.env.NODE_ENV = 'production'
require('chai').should() require('chai').should()

View file

@ -74,12 +74,12 @@ describe('XMPP component', () => {
it('Should connect to XMPP server and join rooms when application start', (done) => { it('Should connect to XMPP server and join rooms when application start', (done) => {
sinon.assert.called(xmppSendStub) sinon.assert.called(xmppSendStub)
// 1 "send" call for presence and n "send" calls for joining rooms // 1 "send" call for presence and n "send" calls for joining rooms
let roomsLength = config.xmpp.rooms.length const roomsLength = config.xmpp.rooms.length
sinon.assert.callCount(xmppSendStub, roomsLength + 1) sinon.assert.callCount(xmppSendStub, roomsLength + 1)
for (let index = 1; index < roomsLength + 1; index++) { for (let index = 1; index < roomsLength + 1; index++) {
const args = xmppSendStub.args[index] const args = xmppSendStub.args[index]
args.should.have.length(1) args.should.have.length(1)
let occupantJid = config.xmpp.rooms[index - 1].id + '/' + 'bot' const occupantJid = config.xmpp.rooms[index - 1].id + '/' + 'bot'
const stanza = xml( const stanza = xml(
'presence', { 'presence', {
to: occupantJid to: occupantJid
@ -353,7 +353,7 @@ describe('XMPP component', () => {
}) })
it('Should call xmpp/client.send() with valid stanza', (done) => { it('Should call xmpp/client.send() with valid stanza', (done) => {
sinon.assert.calledOnce(xmppSendStub) sinon.assert.calledOnce(xmppSendStub)
let stanza = xml( const stanza = xml(
'message', { 'message', {
to: 'someone@domain-xmpp.ltd', to: 'someone@domain-xmpp.ltd',
type: 'chat' type: 'chat'