From 2772b33fa126784fc6e21377786471a918b22dc7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Tue, 6 Dec 2022 12:30:23 +0900 Subject: [PATCH] :v: --- .gitignore | 3 - index.js | 3216 ++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 3 files changed, 3217 insertions(+), 4 deletions(-) create mode 100644 index.js diff --git a/.gitignore b/.gitignore index d7cfcd8..cabce72 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,3 @@ typings/ # Test files run.js - -index.js -*.d.ts diff --git a/index.js b/index.js new file mode 100644 index 0000000..5cbdd18 --- /dev/null +++ b/index.js @@ -0,0 +1,3216 @@ +"use strict"; +// INDEX.JS +/** Copyright (c) 2018 Craig Yamato */ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; + result["default"] = mod; + return result; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/** + * @fileoverview The SyslogPro module for sending syslog messages + * + * Syslog formatting classes can be used as input into a Syslog class to be used + * simultaneously to the same Syslog server. The Syslog Class with a configured + * Syslog server target can also be used as the input into each of the + * formatting classes so that they may run independently. + * @author Craig Yamato + * @copyright (c) 2018 - Craig Yamato + * @version 0.1.0 + * @exports Syslog + * @exports LEEF + * @exports CEF + * @module SyslogPro + */ +const events_1 = __importDefault(require("events")); +const moment_1 = __importDefault(require("moment")); +const dns = __importStar(require("dns")); +const os = __importStar(require("os")); +let dnsPromises = dns.promises; +/** + * Format the ANSI foreground color code from a RGB hex code or ANSI color code + * @private + * @param {string} hex - The color hex code in the form of #FFFFFF or Number of + * the ANSI color code (30-37 Standard & 0-255 Extended) + * @returns {Promise} - The formatted ANSI color code + * @throws {Error} - A Format Error + */ +function rgbToAnsi(hex, extendedColor) { + let colorCode = 0; // Var to hold color code + // Break HEX Code up into RGB + const hexParts = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (hexParts || typeof hex === 'number') { + if (typeof hex === 'number') { + if (extendedColor && hex < 256) { + return hex; + } + else if ((hex > 29 && hex < 38) || (hex > 89 && hex < 98)) { + return hex; + } + else { + throw new Error('FORMAT ERROR: Color code not in range'); + } + } + else { + const r = parseInt(hexParts[1], 16); + const g = parseInt(hexParts[2], 16); + const b = parseInt(hexParts[3], 16); + if (extendedColor) { + if (r === g && g === b) { + // Gray Scale Color + if (r < 8) { + colorCode = 16; + } + else if (r > 248) { + colorCode = 231; + } + else { + colorCode = Math.round(((r - 8) / 247) * 24) + 232; + } + } + else { + colorCode = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + } + } + else { + colorCode = 30; + const red = r / 255; + const green = g / 255; + const blue = b / 255; + let v = Math.max(red, green, blue) * 100; + v = Math.round(v / 50); + if (v === 1) { + colorCode += ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + } + if (v === 2) { + colorCode += 60; + } + } + } + return colorCode; + } + else { + throw new Error('TYPE ERROR: Not in RGB color hex or color code'); + } +} +/** + * A class to work with syslog messages using UDP, TCP, or TLS transport. + * There is support for Syslog message formatting RFC-3164, RFC-5424 including + * Structured Data, IBM LEEF (Log Event Extended Format), and HP CEF (Common + * Event Format). + * Syslog formatting classes can be used as input into a Syslog class to be used + * simultaneously to the same Syslog server. * + * @requires moment + * @version 0.0.0 + * @since 0.0.0 + */ +class Syslog extends events_1.default { + /** + * Construct a new Syslog transport object with user options + * @public + * @version 0.0.0 + * @since 0.0.0 + * @this Syslog + * @param {object} [options] - Options object + * >>>Transport Configuration + * @param {string} [options.target='localhost'] - The IP Address|FQDN of the + * Syslog Server, this option if set will take presidents over any target + * set in a formatting object + * @param {string} [options.protocol='udp'] - L4 transport protocol + * (udp|tcp|tls), this option if set will take presidents over any + * transport set in a formatting object + * @param {number} [options.port=514] - IP port, this option if set will take + * presidents over any IP Port set in a formatting object + * @param {number} [options.tcpTimeout=10000] - Ignored for all other + * transports, this option if set will take presidents over any timeout + * set in a formatting object + * @param {Buffer[]|string[]} [options.tlsServerCerts] - Array of authorized + * TLS server certificates, this option if set will take presidents + * over any certificates set in a formatting object + * @param {string} [options.tlsClientCert] - Client TLS certificate that this + * client should use, this option if set will take presidents over any + * certificates set in a formatting object + * @param {string} [options.tlsClientKey] - Client TLS key that this client + * should use, this option if set will take presidents over any + * certificates set in a formatting object + * @param {string} [options.rejectUnauthorized] - If not false, the server + * certificate is verified against the list of supplied CAs. + * >>>Syslog Format Settings + * @param {string} [options.format='none'] - Valid syslog format options for + * this module are 'none', 'rfc3164', 'rfc5424', 'leef', 'cef' + * @param {RFC3164} [options.rfc5424] - {@link module:SyslogPro~RFC5424| + * RFC5424 related settings} + * @param {RFC5424} [options.rfc5424] - {@link module:SyslogPro~RFC5424| + * RFC5424 related settings} + * @param {LEEF} [options.leef] - {@link module:SyslogPro~LEEF|IBM LEEF + * (Log Event Extended Format) object} + * @param {CEF} [options.cef] - {@link module:SyslogPro~CEF|HP CEF + * (Common Event Format) formatting object} + */ + constructor(options) { + super(); + if (!options) { + options = {}; + } + // Basic transport setup + /** @type {string} */ + this.target = options.target || 'localhost'; + /** @type {string} */ + this.protocol = options.protocol || 'udp'; + this.protocol = this.protocol.toLowerCase(); + /** @type {number} */ + this.port = options.port; + if (!this.port) { + // defaut port defined on https://tools.ietf.org/html/rfc3195 + // and https://tools.ietf.org/html/rfc5425 + if (this.protocol === 'udp') { + this.port = 514; + } + else if (this.protocol === 'tcp') { + this.port = 601; + } + else { + this.port = 6514; + } + } + this.tcpSocketPromise = null; + this.tlsSocketPromise = null; + this.udpSocketPromise = null; + /** @type {number} */ + this.tcpTimeout = options.tcpTimeout || 10000; + if (Array.isArray(options.tlsServerCerts) + || Buffer.isBuffer(options.tlsServerCerts) + || typeof options.tlsServerCerts === 'string') { + this.addTlsServerCerts(options.tlsServerCerts); + } + else { + this.tlsServerCerts = []; + } + if (options.tlsClientCert) { + /** @type {string} */ + this.tlsClientCert = options.tlsClientCert; + } + if (options.tlsClientKey) { + /** @type {string} */ + this.tlsClientKey = options.tlsClientKey; + } + this.rejectUnauthorized = options.rejectUnauthorized !== false; + // Syslog Format + if (typeof options.format === 'string') { + /** @type {string} */ + this.format = options.format.toLowerCase(); + } + else { + this.format = options.format || 'none'; + } + if (options.rfc3164) { + if (options.rfc3164 instanceof RFC3164) { + /** @type {RFC3164} */ + this.rfc3164 = options.rfc3164; + } + else { + this.rfc3164 = new RFC3164(options.rfc3164); + } + } + if (options.rfc5424) { + if (options.rfc5424 instanceof RFC5424) { + /** @type {RFC5424} */ + this.rfc5424 = options.rfc5424; + } + else { + this.rfc5424 = new RFC5424(options.rfc5424); + } + } + if (options.leef) { + if (options.leef instanceof LEEF) { + /** @type {LEEF} */ + this.leef = options.leef; + } + else { + this.leef = new LEEF(options.leef); + } + } + if (options.cef) { + if (options.cef instanceof CEF) { + /** @type {CEF} */ + this.cef = options.cef; + } + else { + this.cef = new CEF(options.cef); + } + } + if (this.format === 'rfc3164' && !this.rfc3164) { + this.rfc3164 = new RFC3164(); + } + if (this.format === 'rfc5424' && !this.rfc5424) { + this.rfc5424 = new RFC5424(); + } + if (this.format === 'leef' && !this.leef) { + this.leef = new LEEF(); + } + if (this.format === 'cef' && !this.cef) { + this.cef = new CEF(); + } + } + /** + * Add a TLS server certificate which can be used to authenticate the server + * this syslog client is connecting too. This function will validate the + * input type and add it to an array of certificates + * @private + * @version 0.0.0 + * @since 0.0.0 + * @param {Buffer|Buffer[]|string|string[]} certs - the certificate(s) + * @returns {boolean} - True + * @throws {Error} - A Type Error + */ + addTlsServerCerts(certs) { + if (Array.isArray(certs)) { + /** @private @type {string[]} */ + this.tlsServerCerts = certs; + } + else if (Buffer.isBuffer(certs) || typeof certs === 'string') { + this.tlsServerCerts = [certs]; + } + else { + let errMsg = 'TYPE ERROR: Server Cert should be a Buffer/string'; + errMsg += ' or array of Buffers/strings'; + throw new Error(errMsg); + } + return true; + } + /** + * Send the Syslog message over UDP + * @private + * @param {string} msg - The formatted Syslog Message + * @returns {Promise} - The Syslog formatted string sent + * @throws {Error} - Network Error + */ + async udpMessage(msg) { + if (!this.udpSocketPromise) { + this.udpSocketPromise = this.udpConnect(); + } + const socket = await this.udpSocketPromise; + return new Promise((resolve, reject) => { + // @ts-ignore + socket.send(msg, (err) => { + if (err) { + reject(err); + } + else { + resolve(msg); + } + }); + }); + } + async udpConnect() { + // Test for target DNS and Address Family (IPv4/6) by looking up the DNS + const dgram = require('dgram'); + const dnsOptions = { + verbatim: true, + }; + const result = await dnsPromises.lookup(this.target, dnsOptions); + const udpType = result.family === 4 ? 'udp4' : 'udp6'; + const socket = dgram.createSocket(udpType); + socket.on('close', () => { + this.onClose(); + }); + socket.on('error', (error) => { + this.emit('error', error); + }); + return new Promise((resolve, reject) => { + socket.connect(this.port, this.target, (err) => { + if (err) { + reject(err); + } + else { + resolve(socket); + } + }); + }); + } + /** + * Send the Syslog message over TCP + * @private + * @param {string} msg - The formatted Syslog Message + * @returns {Promise} - The Syslog formatted string sent + * @throws {Error} - Timeout error for TCP and TLS connections + * @throws {Error} - Network Error + */ + async tcpMessage(msg) { + if (!this.tcpSocketPromise) { + this.tcpSocketPromise = this.tcpConnect(); + } + const socket = await this.tcpSocketPromise; + return new Promise((resolve, reject) => { + const removeListeners = () => { + socket.off('error', onceError); + socket.off('timeout', onceTimeout); + }; + const onceError = (error) => { + removeListeners(); + reject(error); + }; + const onceTimeout = () => { + removeListeners(); + reject(new Error('TIMEOUT ERROR: Syslog server TCP timeout')); + }; + socket.write(msg, () => { + removeListeners(); + resolve(msg); + }); + socket.once('error', onceError); + socket.once('timeout', onceTimeout); + }); + } + async tcpConnect() { + const net = require('net'); + const tcpOptions = { + host: this.target, + port: this.port, + }; + return new Promise((resolve, reject) => { + const removeListeners = () => { + socket.off('error', onceError); + socket.off('timeout', onceTimeout); + }; + const onceError = (error) => { + removeListeners(); + reject(error); + }; + const onceTimeout = () => { + removeListeners(); + reject(new Error('TIMEOUT ERROR: Syslog server TCP timeout')); + }; + const socket = net.connect(tcpOptions, () => { + removeListeners(); + resolve(socket); + }); + socket.setTimeout(this.tcpTimeout); + socket.once('error', onceError); + socket.once('timeout', onceTimeout); + socket.on('close', () => { + this.onClose(); + }); + socket.on('end', () => { + this.emit('end'); + }); + socket.on('error', (error) => { + socket.destroy(); + this.emit('error', error); + }); + socket.on('timeout', () => { + socket.end(); + this.emit('timeout'); + }); + }); + } + /** + * Send the Syslog message over TLS + * @private + * @param {string} msg - The formatted Syslog Message + * @returns {Promise} - The Syslog formatted string sent + * @throws {Error} - Timeout error for TCP and TLS connections + * @throws {Error} - Network Error + */ + async tlsMessage(msg) { + if (!this.tlsSocketPromise) { + this.tlsSocketPromise = this.tlsConnect(); + } + const socket = await this.tlsSocketPromise; + return new Promise((resolve, reject) => { + const removeListeners = () => { + socket.off('error', onceError); + socket.off('timeout', onceTimeout); + }; + const onceError = (error) => { + removeListeners(); + reject(error); + }; + const onceTimeout = () => { + removeListeners(); + reject(new Error('TIMEOUT ERROR: Syslog server TLS timeout')); + }; + socket.write(msg, () => { + removeListeners(); + resolve(msg); + }); + socket.once('error', onceError); + socket.once('timeout', onceTimeout); + }); + } + async tlsConnect() { + const tls = require('tls'); + const tlsOptions = { + host: this.target, + port: this.port, + }; + // Load client cert and key if requested + if (Buffer.isBuffer(this.tlsClientKey) + || typeof this.tlsClientKey === 'string') { + tlsOptions.key = this.tlsClientKey; + } + else if (typeof this.tlsClientKey !== 'undefined') { + const errMsg = 'TYPE ERROR: TLS Client Key is not a valid type'; + throw new Error(errMsg); + } + if (Buffer.isBuffer(this.tlsClientCert) + || typeof this.tlsClientCert === 'string') { + tlsOptions.cert = this.tlsClientCert; + } + else if (typeof this.tlsClientCert !== 'undefined') { + const errMsg = 'TYPE ERROR: TLS Client Cert is not a valid type'; + throw new Error(errMsg); + } + // Load any server certs if provided + let tlsCerts = this.tlsServerCerts.length; + if (tlsCerts > 0) { + const tlsOptionsCerts = []; + for (let certIndex = 0; certIndex < tlsCerts; certIndex++) { + const tlsServerCert = this.tlsServerCerts[certIndex]; + if (!Buffer.isBuffer(tlsServerCert) + && typeof tlsServerCert !== 'string') { + let errMsg = 'TYPE ERROR: TLS Server Cert is not a valid type'; + throw new Error(errMsg); + } + let cert = this.tlsServerCerts[certIndex]; + tlsOptionsCerts.push(cert); + } + tlsOptions.ca = tlsOptionsCerts; + } + tlsOptions.rejectUnauthorized = this.rejectUnauthorized; + return new Promise((resolve, reject) => { + const removeListeners = () => { + socket.off('error', onceError); + socket.off('timeout', onceTimeout); + }; + const onceError = (error) => { + removeListeners(); + reject(error); + }; + const onceTimeout = () => { + removeListeners(); + reject(new Error('TIMEOUT ERROR: Syslog server TLS timeout')); + }; + const socket = tls.connect(tlsOptions, () => { + removeListeners(); + resolve(socket); + }); + socket.setTimeout(this.tcpTimeout); + socket.once('error', onceError); + socket.once('timeout', onceTimeout); + socket.on('close', () => { + this.onClose(); + }); + socket.on('end', () => { + this.emit('end'); + }); + socket.on('error', (error) => { + socket.destroy(); + this.emit('error', error); + }); + socket.on('timeout', () => { + socket.end(); + this.emit('timeout'); + }); + }); + } + /** + * Send the Syslog message to the selected target Syslog server using the + * selected transport. + * @private + * @param {string} msg - The formatted Syslog Message + * @returns {Promise} - The Syslog formatted string sent + * @throws {Error} - Timeout error for TCP and TLS connections + * @throws {Error} - Network Error + */ + async send(msg) { + if (typeof msg !== 'string') { + throw new Error('TYPE ERROR: Syslog message must be a string'); + } + if (this.protocol === 'udp') { + return this.udpMessage(msg); + } + else if (this.protocol === 'tcp') { + return this.tcpMessage(msg); + } + else if (this.protocol === 'tls') { + return this.tlsMessage(msg); + } + else { + let errorMsg = 'FORMAT ERROR: Protocol not recognized, should be '; + errorMsg += 'udp|tcp|tls'; + throw new Error(errorMsg); + } + } + async close() { + if (this.tcpSocketPromise) { + let socket; + try { + socket = await this.tcpSocketPromise; + } + catch (err) { + this.tcpSocketPromise = null; + return; + } + return new Promise((resolve) => { + socket.end(resolve); + }); + } + if (this.tlsSocketPromise) { + let socket; + try { + socket = await this.tlsSocketPromise; + } + catch (err) { + this.tlsSocketPromise = null; + return; + } + return new Promise((resolve) => { + socket.end(resolve); + }); + } + if (this.udpSocketPromise) { + let socket; + try { + socket = await this.udpSocketPromise; + } + catch (err) { + this.udpSocketPromise = null; + return; + } + return new Promise((resolve) => { + socket.close(resolve); + }); + } + } + async onClose() { + this.tcpSocketPromise = null; + this.tlsSocketPromise = null; + this.udpSocketPromise = null; + this.emit('close'); + } +} +exports.Syslog = Syslog; +class Format { + async close() { + return this.server.close(); + } +} +/** + * The base class of RFC formartted syslog messages. + */ +class RFC extends Format { + /** + * Sets the color to be used for messages at a set priority + * @public + * @param {string} [colors.emergencyColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @param {string} [colors.alertColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @param {string} [colors.criticalColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @param {string} [colors.errorColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @param {string} [colors.warningColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @param {string} [colors.noticeColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @param {string} [colors.informationalColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [colors.debugColor] - A RGB Hex coded color in the form + * of #FFFFFF or as or the ANSI color code number (30-37 Standard & 0-255 + * Extended) + * @throws {Error} A standard error object + */ + setColor(colors, extendedColor) { + if (colors.emergencyColor) { + try { + this.emergencyColor = rgbToAnsi(colors.emergencyColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'emergencyColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.alertColor) { + try { + this.alertColor = rgbToAnsi(colors.alertColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'alertColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.criticalColor) { + try { + this.criticalColor = rgbToAnsi(colors.criticalColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'criticalColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.errorColor) { + try { + this.errorColor = rgbToAnsi(colors.errorColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'errorColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.warningColor) { + try { + this.warningColor = rgbToAnsi(colors.warningColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'warningColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.noticeColor) { + try { + this.noticeColor = rgbToAnsi(colors.noticeColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'noticeColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.informationalColor) { + try { + this.informationalColor = rgbToAnsi(colors.informationalColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'informationalColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + if (colors.debugColor) { + try { + this.debugColor = rgbToAnsi(colors.debugColor, this.extendedColor); + } + catch (reason) { + reason.message = 'TYPE ERROR: '; + reason.message += 'debugColor'; + reason.message += ' Not in RGB color hex or color code'; + throw reason; + } + } + return true; + } +} +/** + * A class to work with RFC3164 formatted syslog messages. The messaging is + * fully configurable and ANSI foreground colors can be added. Both ANSI 8 and + * ANSI 256 color are fully supported. + * + * A Syslog class with a configured + * Syslog server target can also be used as the input into the formatting + * classes so that it may run independently. + * + * The RFC3164 Syslog logging format is meant to be used as a stream of log data + * from a service or application. This class is designed to be used in this + * fashion where new messages are written to the class as needed. + * @requires moment + * @version 0.0.0 + * @since 0.0.0 + */ +class RFC3164 extends RFC { + /** + * Construct a new RFC3164 formatted Syslog object with user options + * @public + * @this RFC3164 + * @param {object} [options] - Options object + * @param {string} [options.applicationName='NodeJSLogger'] - Application + * @param {string} [options.hostname=os.hostname] - The name of this server + * @param {number} [options.facility=23] - Facility code to use sending this + * message + * @param {boolean} [options.color=false] - Apply color coding encoding tag + * with syslog message text + * @param {boolean} [options.extendedColor=false] - Use the extended ANSI + * color set encoding tag with syslog message text + * @param {object} [options.colors] - User defended colors for + * severities + * @param {string} [options.colors.emergencyColor] - A RGB Hex coded color in + * the form of #FFFFFF or as or the ANSI color code number (30-37 Standard + * & 0-255 Extended) + * @param {string} [options.colors.alertColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [options.colors.criticalColor] - A RGB Hex coded color in + * the form of #FFFFFF or as or the ANSI color code number (30-37 Standard + * & 0-255 Extended) + * @param {string} [options.colors.errorColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [options.colors.warningColor] - A RGB Hex coded color in + * the form of #FFFFFF or as or the ANSI color code number (30-37 Standard + * & 0-255 Extended) + * @param {string} [options.colors.noticeColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [options.colors.informationalColor] - A RGB Hex coded color + * in the form of #FFFFFF or as or the ANSI color code number (30-37 + * Standard & 0-255 Extended) + * @param {string} [options.colors.debugColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {Syslog} [options.server=false] - A {@link module:SyslogPro~Syslog| + * Syslog server connection} that should be used to send messages directly + * from this class. @see SyslogPro~Syslog + */ + constructor(options) { + super(); + options = options || {}; + this.hostname = options.hostname || os.hostname(); + this.applicationName = options.applicationName || ''; + this.facility = options.facility || 23; + if (options.color) { + /** @type {boolean} */ + this.color = true; + } + else { + this.color = false; + } + if (options.extendedColor) { + /** @type {boolean} */ + this.extendedColor = true; + } + else { + this.extendedColor = false; + } + if (options.server instanceof Syslog) { + /** @private @type {Syslog} */ + this.server = options.server; + } + else { + this.server = new Syslog(options.server); + } + if (this.server.protocol === 'tcp' || this.server.protocol === 'tls') { + this.octetCounting = options.octetCounting !== false; + } + else { + this.octetCounting = false; + } + if (this.extendedColor) { + /** @private @type {number} */ + this.emergencyColor = 1; // Red foreground color + /** @private @type {number} */ + this.alertColor = 202; // Dark Orange foreground color + /** @private @type {number} */ + this.criticalColor = 208; // Orange foreground color + /** @private @type {number} */ + this.errorColor = 178; // Light Orange foreground color + /** @private @type {number} */ + this.warningColor = 226; // Yellow foreground color + /** @private @type {number} */ + this.noticeColor = 117; // Light Blue foreground color + /** @private @type {number} */ + this.informationalColor = 45; // Blue foreground color + /** @private @type {number} */ + this.debugColor = 27; // Dark Blue foreground color + } + else { + this.emergencyColor = 31; // Red foreground color + this.alertColor = 31; // Red foreground color + this.criticalColor = 31; // Red foreground color + this.errorColor = 33; // Yellow foreground color + this.warningColor = 33; // Yellow foreground color + this.noticeColor = 36; // Blue foreground color + this.informationalColor = 36; // Blue foreground color + this.debugColor = 34; // Dark Blue foreground color + } + if (typeof options.colors === 'object') { + this.setColor(options.colors, this.extendedColor); + } + } + /** + * Building a formatted message. Returns a promise with a formatted message + * @public + * @param {string} msg - The Syslog Message + * @param {object} [options] - Options object + * @param {number} [options.severity=7] - An array of structure + * @param {number} [options.colorCode=36] - The ANSI color code to use if + * message coloration is selected + * @param {Date} [options.timestamp] - The timestamp to use + * @param {string} [options.hostname] - The hostname + * @param {string} [options.applicationName] - The application name + * @returns {string} A Syslog formatted string according to the selected RFC + * @throws {Error} A standard error object + */ + buildMessage(msg, options) { + options = options || {}; + let severity = typeof options.severity === 'number' ? + options.severity : 6; + if (typeof msg !== 'string' || options.msgSeverity > 7) { + let errMsg = 'FORMAT ERROR: Syslog message must be a string'; + errMsg += ' msgSeverity must be a number between 0 and 7'; + throw new Error(errMsg); + } + let fmtMsg = ''; // Formatted Syslog message string var + const newLine = '\n'; + const escapeCode = '\u001B'; + const resetColor = '\u001B[0m'; + // The PRI is common to both RFC formats + const pri = (this.facility * 8) + severity; + // Add requested color + if (this.color) { + options.msgColor = options.msgColor || 36; + let colorCode = '['; + if (this.extendedColor) { + colorCode += '38;5;'; // Extended 256 Colors ANSI Code + } + if (typeof options.msgColor === 'number') { + colorCode += options.msgColor; + colorCode += 'm'; // ANSI Color Closer + } + else { + colorCode = '[39m'; // Use terminal's default color + } + msg = escapeCode + colorCode + msg + resetColor; + } + // RegEx to find a leading 0 in the day of a DateTime for RFC3164 RFC3164 + // uses BSD timeformat + const rfc3164DateRegEx = /((A|D|F|J|M|N|O|S)(a|c|e|p|o|u)(b|c|g|l|n|p|r|t|v|y)\s)0(\d\s\d\d:\d\d:\d\d)/; + const timestamp = moment_1.default(options.timestamp) + .format('MMM DD hh:mm:ss') + .replace(rfc3164DateRegEx, '$1 $5'); + const hostname = options.hostname || this.hostname; + const applicationName = options.applicationName || this.applicationName; + // Build message + fmtMsg = '<' + pri + '>'; + fmtMsg += timestamp; + fmtMsg += ' ' + hostname; + fmtMsg += ' ' + applicationName; + fmtMsg += ' ' + msg; + return this.octetCounting + ? `${Buffer.byteLength(fmtMsg)} ${fmtMsg}` + : fmtMsg + newLine; + } + /** + * send a RFC5424 formatted message. Returns a promise with the formatted + * message that was sent. If no server connection was defined when the + * class was created a default Syslog connector will be used. + * @see SyslogPro~Syslog + * @public + * @param {string} msg - The unformatted Syslog message to send + * @param {object} [options] - Options object + * @param {number} [options.severity=7] - An array of structure + * @param {number} [options.colorCode=36] - The ANSI color code to use if + * @returns {Promise} A Syslog formatted string according to the selected RFC + * @throws {Error} A standard error object + */ + async send(msg, options) { + const result = this.buildMessage(msg, options); + return this.server.send(result); + } + /** + * Send a syslog message with a security level of 0 (Emergency) + * @public + * @param {string} msg - The emergency message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + emergency(msg) { + return this.send(msg, { + severity: 0, + colorCode: this.emergencyColor, + }); + } + /** + * Send a syslog message with a security level of 0 (Emergency) + * @public + * @param {string} msg - The emergency message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + emer(msg) { + return this.emergency(msg); + } + /** + * Send a syslog message with a severity level of 1 (Alert) + * @public + * @param {string} msg - The alert message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + alert(msg) { + return this.send(msg, { + severity: 1, + colorCode: this.alertColor, + }); + } + /** + * Send a syslog message with a severity level of 2 (Critical) + * @public + * @param {string} msg - The critical message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + critical(msg) { + return this.send(msg, { + severity: 2, + colorCode: this.criticalColor, + }); + } + /** + * Send a syslog message with a severity level of 2 (Critical) + * @public + * @param {string} msg - The critical message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + crit(msg) { + return this.critical(msg); + } + /** + * Send a syslog message with a severity level of 3 (Error) + * @public + * @param {string} msg - The error message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + error(msg) { + return this.send(msg, { + severity: 3, + colorCode: this.errorColor, + }); + } + /** + * Send a syslog message with a severity level of 3 (Error) + * @public + * @param {string} msg - The error message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + err(msg) { + return this.error(msg); + } + /** + * Send a syslog message with a severity level of 4 (Warning) + * @public + * @param {string} msg - The warning message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + warning(msg) { + return this.send(msg, { + severity: 4, + colorCode: this.warningColor, + }); + } + /** + * Send a syslog message with a severity level of 4 (Warning) + * @public + * @param {string} msg - The warning message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + warn(msg) { + return this.warning(msg); + } + /** + * Send a syslog message with a severity level of 5 (Notice) + * @public + * @param {string} msg - The notice message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + notice(msg) { + return this.send(msg, { + severity: 5, + colorCode: this.noticeColor, + }); + } + /** + * Send a syslog message with a severity level of 5 (Notice) + * @public + * @param {string} msg - The notice message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + note(msg) { + return this.notice(msg); + } + /** + * Send a syslog message with a severity level of 6 (Informational) + * @public + * @param {string} msg - The informational message to send to the Syslog + * server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + informational(msg) { + return this.send(msg, { + severity: 6, + colorCode: this.informationalColor, + }); + } + /** + * Send a syslog message with a severity level of 6 (Informational) + * @public + * @param {string} msg - The informational message to send to the Syslog + * server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + info(msg) { + return this.informational(msg); + } + /** + * Send a syslog message with a severity level of 6 (Informational) + * @public + * @param {string} msg - The informational message to send to the Syslog + * server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + log(msg) { + return this.informational(msg); + } + /** + * Send a syslog message with a severity level of 7 (Debug) + * @public + * @param {string} msg - The debug message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + debug(msg) { + return this.send(msg, { + severity: 7, + colorCode: this.debugColor, + }); + } +} +exports.RFC3164 = RFC3164; +/** + * A class to work with RFC5424 formatted syslog messages. The messaging is + * fully configurable and ANSI foreground * colors can be added. Both ANSI 8 + * and ANSI 256 color are fully supported. + * + * A Syslog class with a configured + * Syslog server target can also be used as the input into the formatting + * classes so that it may run independently. + * + * The RFC5424 Syslog logging format is meant to be used as a stream of log data + * from a service or application. This class is designed to be used in this + * fashion where new messages are written to the class as needed. + * @requires moment + * @version 0.0.0 + * @since 0.0.0 + */ +class RFC5424 extends RFC { + /** + * Construct a new RFC5424 formatted Syslog object with user options + * @public + * @this RFC5424 + * @param {object} [options] - Options object + * @param {string} [options.applicationName='NodeJSLogger'] - Application + * @param {string} [options.hostname=os.hostname] - The name of this server + * @param {boolean} [options.timestamp=true] - Included a Timestamp + * @param {boolean} [options.timestampUTC=true] - RFC standard is for + * local time + * @param {boolean} [options.timestampMS=true] - Timestamp with ms + * resolution + * @param {boolean} [options.timestampTZ=true] - Should the timestamp + * included time zone + * @param {boolean} [options.utf8BOM=true] - Included the UTF8 + * @param {boolean} [options.color=false] - Included the UTF8 + * @param {boolean} [options.extendedColor=false] - Included the UTF8 + * encoding tag with syslog message text + * @param {object} [options.colors] - User defended colors for + * severities + * @param {string} [options.colors.emergencyColor] - A RGB Hex coded color in + * the form of #FFFFFF or as or the ANSI color code number (30-37 Standard + * & 0-255 Extended) + * @param {string} [options.colors.alertColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [options.colors.criticalColor] - A RGB Hex coded color in + * the form of #FFFFFF or as or the ANSI color code number (30-37 Standard + * & 0-255 Extended) + * @param {string} [options.colors.errorColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [options.colors.warningColor] - A RGB Hex coded color in + * the form of #FFFFFF or as or the ANSI color code number (30-37 Standard + * & 0-255 Extended) + * @param {string} [options.colors.noticeColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {string} [options.colors.informationalColor] - A RGB Hex coded color + * in the form of #FFFFFF or as or the ANSI color code number (30-37 + * Standard & 0-255 Extended) + * @param {string} [options.colors.debugColor] - A RGB Hex coded color in the + * form of #FFFFFF or as or the ANSI color code number (30-37 Standard & + * 0-255 Extended) + * @param {Syslog} [options.server=false] - A {@link module:SyslogPro~Syslog| + * Syslog server connection} that should be used to send messages directly + * from this class. @see SyslogPro~Syslog + */ + constructor(options) { + super(); + options = options || {}; + this.hostname = options.hostname || os.hostname(); + this.applicationName = options.applicationName || '-'; + this.timestamp = options.timestamp !== false; + this.timestampMS = options.timestampMS !== false; + this.timestampTZ = options.timestampTZ !== false; + this.timestampUTC = options.timestampUTC !== false; + if (typeof options.utf8BOM === 'undefined' || options.utf8BOM) { + /** @type {boolean} */ + this.utf8BOM = true; + } + else { + this.utf8BOM = false; + } + if (options.color) { + /** @type {boolean} */ + this.color = true; + } + else { + this.color = false; + } + if (options.extendedColor) { + /** @type {boolean} */ + this.extendedColor = true; + } + else { + this.extendedColor = false; + } + if (options.server instanceof Syslog) { + /** @private @type {Syslog} */ + this.server = options.server; + } + else { + this.server = new Syslog(options.server); + } + if (this.server.protocol === 'tcp' || this.server.protocol === 'tls') { + this.octetCounting = options.octetCounting !== false; + } + else { + this.octetCounting = false; + } + if (this.extendedColor) { + /** @private @type {number} */ + this.emergencyColor = 1; // Red foreground color + /** @private @type {number} */ + this.alertColor = 202; // Dark Orange foreground color + /** @private @type {number} */ + this.criticalColor = 208; // Orange foreground color + /** @private @type {number} */ + this.errorColor = 178; // Light Orange foreground color + /** @private @type {number} */ + this.warningColor = 226; // Yellow foreground color + /** @private @type {number} */ + this.noticeColor = 117; // Light Blue foreground color + /** @private @type {number} */ + this.informationalColor = 45; // Blue foreground color + /** @private @type {number} */ + this.debugColor = 27; // Dark Blue foreground color + } + else { + this.emergencyColor = 31; // Red foreground color + this.alertColor = 31; // Red foreground color + this.criticalColor = 31; // Red foreground color + this.errorColor = 33; // Yellow foreground color + this.warningColor = 33; // Yellow foreground color + this.noticeColor = 36; // Blue foreground color + this.informationalColor = 36; // Blue foreground color + this.debugColor = 34; // Dark Blue foreground color + } + if (typeof options.colors === 'object') { + this.setColor(options.colors, this.extendedColor); + } + } + /** + * Building a formatted message. Returns a promise with a formatted message + * @public + * @param {?string} msg - The Syslog Message + * @param {object} [options] - Options object + * @param {number} [options.severity=7] - An array of structure + * @param {number} [options.facility=23] - Facility code to use sending this + * message + * @param {string} [options.pid='-'] - The process id of the service sending + * this message + * @param {object} [options.structuredData] - An object of structure + * data objects conforming to the IETF/IANA defined SD-IDs or IANA + * registered SMI Network Management Private Enterprise Code SD-ID + * conforming to the format + * [name@ parameter=value] + * @param {number} [options.colorCode=36] - The ANSI color code to use if + * message coloration is selected + * @param {Date} [options.timestamp] - The timestamp to use + * @param {string} [options.hostname] - The hostname + * @param {string} [options.applicationName] - The application name + * @returns {string} A Syslog formatted string according to the selected RFC + * @throws {Error} A standard error object + */ + buildMessage(msg, options) { + options = options || {}; + let severity = typeof options.severity === 'number' ? + options.severity : 6; + if ((msg != null && typeof msg !== 'string') || options.severity > 7) { + let errMsg = 'FORMAT ERROR: Syslog message must be a string'; + errMsg += ' msgSeverity must be a number between 0 and 7'; + throw new Error(errMsg); + } + let facility = options.facility || 23; + let hostname = options.hostname || this.hostname; + let applicationName = options.applicationName || this.applicationName; + let pid = options.pid || '-'; + let id = options.id || '-'; + let fmtMsg = ''; // Formated Syslog message string var + const newLine = '\n'; + const escapeCode = '\u001B'; + const resetColor = '\u001B[0m'; + // The PRI is common to both RFC formats + const pri = (facility * 8) + severity; + // Add requested color + if (this.color && msg) { + options.msgColor = options.msgColor || 36; + let colorCode = '['; + if (this.extendedColor) { + colorCode += '38;5;'; // Extended 256 Colors ANSI Code + } + if (typeof options.msgColor === 'number') { + colorCode += options.msgColor; + colorCode += 'm'; // ANSI Color Closer + } + else { + colorCode = '[39m'; // Use terminal's default color + } + msg = escapeCode + colorCode + msg + resetColor; + } + // RFC5424 timestamp formating + let timestamp = '-'; + if (this.timestamp || options.timestamp) { + if (this.timestampUTC) { + if (this.timestampMS) { + if (this.timestampTZ) { + timestamp = moment_1.default(options.timestamp).toISOString(); + } + else { + timestamp = moment_1.default(options.timestamp) + .utc() + .format('YYYY-MM-DDThh:mm:ss.SSS'); + } + } + else { + if (this.timestampTZ) { + timestamp = moment_1.default(options.timestamp) + .utc() + .format('YYYY-MM-DDThh:mm:ss[Z]'); + } + else { + timestamp = moment_1.default(options.timestamp) + .utc() + .format('YYYY-MM-DDThh:mm:ss'); + } + } + } + else { + if (this.timestampTZ) { + if (this.timestampMS) { + timestamp = moment_1.default(options.timestamp) + .format('YYYY-MM-DDThh:mm:ss.SSSZ'); + } + else { + timestamp = moment_1.default(options.timestamp) + .format('YYYY-MM-DDThh:mm:ssZ'); + } + } + else { + if (this.timestampMS) { + timestamp = moment_1.default(options.timestamp) + .format('YYYY-MM-DDThh:mm:ss.SSS'); + } + else { + timestamp = moment_1.default(options.timestamp).format('YYYY-MM-DDThh:mm:ss'); + } + } + } + } + // Build Structured Data string + const structuredData = RFC5424.buildStructuredData(options.structuredData); + // Build the message + fmtMsg = '<' + pri + '>'; + fmtMsg += '1'; // Version number + fmtMsg += ' ' + timestamp; + fmtMsg += ' ' + hostname; + fmtMsg += ' ' + applicationName; + fmtMsg += ' ' + pid; + fmtMsg += ' ' + id; + fmtMsg += ' ' + structuredData; + if (msg) { + fmtMsg += ` ${this.utf8BOM ? 'BOM' : ''}${msg}`; + } + return this.octetCounting + ? `${Buffer.byteLength(fmtMsg)} ${fmtMsg}` + : fmtMsg + newLine; + } + static buildStructuredData(data) { + // Build Structured Data string + let result; + if (data) { + result = Object.entries(data).map(([sdId, sdParam]) => { + return '[' + [ + sdId, + ...Object.entries(sdParam) + .reduce((array, [name, value]) => { + if (typeof value === 'undefined') { + return array; + } + return [ + ...array, + ...(Array.isArray(value) ? value : [value]) + .map((v) => `${name}="${RFC5424.escapeParamValue(v || '')}"`), + ]; + }, []), + ].join(' ') + ']'; + }).join(''); + } + return result || '-'; + } + static escapeParamValue(value) { + return String(value) + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\\"') + .replace(/]/g, '\\]'); + } + /** + * send a RFC5424 formatted message. Returns a promise with the formatted + * message that was sent. If no server connection was defined when the + * class was created a default Syslog connector will be used. + * @see SyslogPro~Syslog + * @public + * @param {?string} msg - The unformatted Syslog message to send + * @returns {Promise} A Syslog formatted string according to the selected RFC + * @throws {Error} A standard error object + */ + async send(msg, options) { + const result = this.buildMessage(msg, options); + return this.server.send(result); + } + /** + * Send a syslog message with a severity level of 0 (Emergency) + * @public + * @param {?string} msg - The emergency message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + emergency(msg) { + return this.send(msg, { + severity: 0, + colorCode: this.emergencyColor, + }); + } + /** + * Send a syslog message with a severity level of 0 (Emergency) + * @public + * @param {?string} msg - The emergency message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + emer(msg) { + return this.emergency(msg); + } + /** + * Send a syslog message with a severity level of 1 (Alert) + * @public + * @param {?string} msg - The alert message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + alert(msg) { + return this.send(msg, { + severity: 1, + colorCode: this.alertColor, + }); + } + /** + * Send a syslog message with a severity level of 2 (Critical) + * @public + * @param {?string} msg - The critical message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + critical(msg) { + return this.send(msg, { + severity: 2, + colorCode: this.criticalColor, + }); + } + /** + * Send a syslog message with a severity level of 2 (Critical) + * @public + * @param {?string} msg - The critical message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + crit(msg) { + return this.critical(msg); + } + /** + * Send a syslog message with a severity level of 3 (Error) + * @public + * @param {?string} msg - The error message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + error(msg) { + return this.send(msg, { + severity: 3, + colorCode: this.errorColor, + }); + } + /** + * Send a syslog message with a severity level of 3 (Error) + * @public + * @param {?string} msg - The error message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + err(msg) { + return this.error(msg); + } + /** + * Send a syslog message with a severity level of 4 (Warning) + * @public + * @param {?string} msg - The warning message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + warning(msg) { + return this.send(msg, { + severity: 4, + colorCode: this.warningColor, + }); + } + /** + * Send a syslog message with a severity level of 4 (Warning) + * @public + * @param {?string} msg - The warning message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + warn(msg) { + return this.warning(msg); + } + /** + * Send a syslog message with a severity level of 5 (Notice) + * @public + * @param {?string} msg - The notice message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + notice(msg) { + return this.send(msg, { + severity: 5, + colorCode: this.noticeColor, + }); + } + /** + * Send a syslog message with a severity level of 5 (Notice) + * @public + * @param {?string} msg - The notice message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + note(msg) { + return this.notice(msg); + } + /** + * Send a syslog message with a severity level of 6 (Informational) + * @public + * @param {?string} msg - The informational message to send to the Syslog + * server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + informational(msg) { + return this.send(msg, { + severity: 6, + colorCode: this.informationalColor, + }); + } + /** + * Send a syslog message with a severity level of 6 (Informational) + * @public + * @param {?string} msg - The informational message to send to the Syslog + * server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + info(msg) { + return this.informational(msg); + } + /** + * Send a syslog message with a severity level of 6 (Informational) + * @public + * @param {?string} msg - The informational message to send to the Syslog + * server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + log(msg) { + return this.informational(msg); + } + /** + * Send a syslog message with a severity level of 7 (Debug) + * @public + * @param {?string} msg - The debug message to send to the Syslog server + * @returns {Promise} - The formatted syslog message sent to the Syslog server + * @throws {Error} - Any bubbled-up error + */ + debug(msg) { + return this.send(msg, { + severity: 7, + colorCode: this.debugColor, + }); + } +} +exports.RFC5424 = RFC5424; +/** + * A class to work with IBM LEEF (Log Event Extended Format) messages this form + * of system messages are designed to work with security systems. Messages can + * be saved to file (Saving to file if not part of this module but a LEEF + * formatted message produced by this module can be saved externally to it) or + * sent via Syslog. + * + * A Syslog class with a configured Syslog server target can also be used as + * the input into the formatting classes so that it may run independently. The + * LEEF format is designed to send event data to a SIEM system and should not + * be as a logging stream. This class is meant to be used once per message. + * @requires moment + * @version 0.0.0 + * @since 0.0.0 + */ +class LEEF extends Format { + /** + * Construct a new LEEF formatting object with user options + * @public + * @param {object} [options] - Options object + * @param {string} [options.vendor='unknown'] - The vendor of the system that + * generated the event being reported + * @param {string} [options.product='unknown'] - The product name of the + * system that genrated the event being reported + * @param {string} [options.version='unknown'] - The version name of the + * system that genrated the event being reported + * @param {string} [options.eventId='unknown'] - The eventId of the + * system that genrated the event being reported + * @param {object} [options.attributes] - LEEF message attributes which + * defaults to all base attributes with null values, new attributes should + * be added as new elements to this object + * @param {boolean} [options.syslogHeader='true'] - Should the LEEF message + * include a Syslog header with Timestamp and source + * @param {Syslog} [options.server=false] - A {@link module:SyslogPro~Syslog| + * Syslog server connection} that should be used to send messages directly + * from this class. @see SyslogPro~Syslog + */ + constructor(options) { + super(); + options = options || {}; + /** @type {string} */ + this.vendor = options.vendor || 'unknown'; + /** @type {string} */ + this.product = options.product || 'unknown'; + /** @type {string} */ + this.version = options.version || 'unknown'; + /** @type {string} */ + this.eventId = options.eventId || 'unknown'; + /** @type {boolean} */ + this.syslogHeader = typeof options.syslogHeader === 'boolean' + ? options.syslogHeader : true; + /** @type {object} */ + this.attributes = options.attributes || { + cat: null, + devTime: null, + devTimeFormat: null, + proto: null, + sev: null, + src: null, + dst: null, + srcPort: null, + dstPort: null, + srcPreNAT: null, + dstPreNAT: null, + srcPostNAT: null, + dstPostNAT: null, + usrName: null, + srcMAC: null, + dstMAC: null, + srcPreNATPort: null, + dstPreNATPort: null, + srcPostNATPort: null, + dstPostNATPort: null, + identSrc: null, + identHostName: null, + identNetBios: null, + identGrpName: null, + identMAC: null, + vSrc: null, + vSrcName: null, + accountName: null, + srcBytes: null, + dstBytes: null, + srcPackets: null, + dstPackets: null, + totalPackets: null, + role: null, + realm: null, + policy: null, + resource: null, + url: null, + groupID: null, + domain: null, + isLoginEvent: null, + isLogoutEvent: null, + identSecondlp: null, + calLanguage: null, + AttributeLimits: null, + calCountryOrRegion: null, + }; + if (options.server instanceof Syslog) { + /** @private @type {Syslog} */ + this.server = options.server; + } + else { + this.server = new Syslog(options.server); + } + } + /** + *Build a formatted message + * @public + * @return {string} - string with formatted message + */ + buildMessage() { + let fmtMsg = 'LEEF:2.0'; + fmtMsg += '|' + this.vendor; + fmtMsg += '|' + this.product; + fmtMsg += '|' + this.version; + fmtMsg += '|' + this.eventId; + fmtMsg += '|'; + // Build LEEF Attributes + const Tab = '\x09'; + const leefAttribs = Object.entries(this.attributes); + const leefAttribsLen = leefAttribs.length; + for (let attrib = 0; attrib < leefAttribsLen; attrib++) { + if (leefAttribs[attrib][1] !== null) { + fmtMsg += leefAttribs[attrib][0] + '=' + leefAttribs[attrib][1] + Tab; + } + } + return fmtMsg; + } + /** + * @public + */ + async send(options) { + const result = this.buildMessage(); + return this.server.send(result); + } +} +exports.LEEF = LEEF; +/** + * A class to work with HP CEF (Common Event Format) messages. This form + * of system messages are designed to work with security systems. Messages can + * be saved to file (Saving to file if not part of this module but a CEF + * formatted message produced by this module can be saved externally to it) or + * sent via Syslog. + * + * A Syslog class with a configured Syslog server target can also be used as + * the input into the formatting classes so that it may run independently. The + * CEF format is designed to send event data to a SIEM system and should not be + * as a logging stream. This class is meant to be used once per message. + * @requires moment + * @version 0.0.0 + * @since 0.0.0 + */ +class CEF extends Format { + /** + * Construct a new CEF formatting object with user options + * @public + * @param {object} [options] - Options object + * @param {string} [options.deviceVendor='unknown'] - The vendor of the system + * that generated the event being reported + * @param {string} [options.deviceProduct='unknown'] - The product name of the + * system that genrated the event being reported + * @param {string} [options.deviceVersion='unknown'] - The version name of the + * system that genrated the event being reported + * @param {string} [options.deviceEventClassId='unknown'] - The eventId of the + * system that genrated the event being reported + * @param {string} [options.name='unknown'] - Name of the service generating + * the notice + * @param {string} [options.severity='unknown'] - Severity of the notification + * @param {string} [options.extensions={}] - Any CEF Key=Value extensions + * @param {Syslog} [options.server=false] - A {@link module:SyslogPro~Syslog| + * Syslog server connection} that should be used to send messages directly + * from this class. @see SyslogPro~Syslog + */ + constructor(options) { + super(); + options = options || {}; + /** @type {string} */ + this.deviceVendor = options.deviceVendor || 'Unknown'; + /** @type {string} */ + this.deviceProduct = options.deviceProduct || 'Unknown'; + /** @type {string} */ + this.deviceVersion = options.deviceVersion || 'Unknown'; + /** @type {string} */ + this.deviceEventClassId = options.deviceEventClassId || 'Unknown'; + /** @type {string} */ + this.name = options.name || 'Unknown'; + /** @type {string} */ + this.severity = options.severity || 'Unknown'; + /** @type {object} */ + this.extensions = options.extensions || { + deviceAction: null, + applicationProtocol: null, + deviceCustomIPv6Address1: null, + 'deviceCustomIPv6 Address1Label': null, + deviceCustomIPv6Address3: null, + 'deviceCustomIPv6Address3 Label': null, + 'deviceCustomIPv6 Address4': null, + 'deviceCustomIPv6 Address4Label': null, + deviceEventCategory: null, + deviceCustomFloatingPoint1: null, + 'deviceCustom FloatingPoint1Label': null, + deviceCustomFloatingPoint2: null, + 'deviceCustomFloatingPoint2 Label': null, + deviceCustomFloatingPoint3: null, + 'deviceCustom FloatingPoint3Label': null, + deviceCustomFloatingPoint4: null, + 'deviceCustom FloatingPoint4Label': null, + deviceCustomNumber1: null, + deviceCustomNumber1Label: null, + DeviceCustomNumber2: null, + deviceCustomNumber2Label: null, + deviceCustomNumber3: null, + deviceCustomNumber3Label: null, + baseEventCount: null, + deviceCustomString1: null, + deviceCustomString1Label: null, + deviceCustomString2: null, + deviceCustomString2Label: null, + deviceCustomString3: null, + deviceCustomString3Label: null, + deviceCustomString4: null, + deviceCustomString4Label: null, + deviceCustomString5: null, + deviceCustomString5Label: null, + deviceCustomString6: null, + deviceCustomString6Label: null, + destinationDnsDomain: null, + destinationServiceName: null, + 'destinationTranslated Address': null, + destinationTranslatedPort: null, + deviceCustomDate1: null, + deviceCustomDate1Label: null, + deviceCustomDate2: null, + deviceCustomDate2Label: null, + deviceDirection: null, + deviceDnsDomain: null, + deviceExternalId: null, + deviceFacility: null, + deviceInboundInterface: null, + deviceNtDomain: null, + deviceOutboundInterface: null, + devicePayloadId: null, + deviceProcessName: null, + deviceTranslatedAddress: null, + destinationHostName: null, + destinationMacAddress: null, + destinationNtDomain: null, + destinationProcessId: null, + destinationUserPrivileges: null, + destinationProcessName: null, + destinationPort: null, + destinationAddress: null, + deviceTimeZone: null, + destinationUserId: null, + destinationUserName: null, + deviceAddress: null, + deviceHostName: null, + deviceMacAddress: null, + deviceProcessId: null, + endTime: null, + externalId: null, + fileCreateTime: null, + fileHash: null, + fileId: null, + fileModificationTime: null, + filePath: null, + filePermission: null, + fileType: null, + flexDate1: null, + flexDate1Label: null, + flexString1: null, + flexString1Label: null, + flexString2: null, + flexString2Label: null, + filename: null, + fileSize: null, + bytesIn: null, + message: null, + oldFileCreateTime: null, + oldFileHash: null, + oldFileId: null, + oldFileModificationTime: null, + oldFileName: null, + oldFilePath: null, + oldFileSize: null, + oldFileType: null, + bytesOut: null, + eventOutcome: null, + transportProtocol: null, + Reason: null, + requestUrl: null, + requestClientApplication: null, + requestContext: null, + requestCookies: null, + requestMethod: null, + deviceReceiptTime: null, + sourceHostName: null, + sourceMacAddress: null, + sourceNtDomain: null, + sourceDnsDomain: null, + sourceServiceName: null, + sourceTranslatedAddress: null, + sourceTranslatedPort: null, + sourceProcessId: null, + sourceUserPrivileges: null, + sourceProcessName: null, + sourcePort: null, + sourceAddress: null, + startTime: null, + sourceUserId: null, + sourceUserName: null, + type: null, + agentDnsDomain: null, + agentNtDomain: null, + agentTranslatedAddress: null, + 'agentTranslatedZone ExternalID': null, + agentTranslatedZoneURI: null, + agentZoneExternalID: null, + agentZoneURI: null, + agentAddress: null, + agentHostName: null, + agentId: null, + agentMacAddress: null, + agentReceiptTime: null, + agentType: null, + agentTimeZone: null, + agentVersion: null, + customerExternalID: null, + customerURI: null, + 'destinationTranslated ZoneExternalID': null, + 'destinationTranslated ZoneURI': null, + destinationZoneExternalID: null, + destinationZoneURI: null, + 'deviceTranslatedZone ExternalID': null, + deviceTranslatedZoneURI: null, + deviceZoneExternalID: null, + deviceZoneURI: null, + destinationGeoLatitude: null, + destinationGeoLongitude: null, + eventId: null, + rawEvent: null, + sourceGeoLatitude: null, + sourceGeoLongitude: null, + 'sourceTranslatedZone ExternalID': null, + sourceTranslatedZoneURI: null, + sourceZoneExternalID: null, + sourceZoneURI: null, + }; + if (options.server instanceof Syslog) { + /** @private @type {Syslog} */ + this.server = options.server; + } + else { + this.server = new Syslog(options.server); + } + } + /** + * Validate this CEF object + * @public + * @return {Promise} - True if validated + * @throws {Error} - First element to fail validation + */ + validate() { + const Extensions = { + deviceAction: { + key: 'act', + type: 'String', + len: 63, + discription: 'Action taken by the device.', + }, + applicationProtocol: { + key: 'app', + type: 'String', + len: 31, + discription: 'Application level protocol, example values are HTTP, ' + + 'HTTPS, SSHv2, Telnet, POP, IMPA, IMAPS, and so on.', + }, + deviceCustomIPv6Address1: { + key: 'c6a1', + type: 'String', + len: null, + discription: 'One of four IPv6 address fields available to map ' + + 'fields that do not apply to any other in this dictionary. ' + + 'TIP: See the guidelines under “User-Defined Extensions” for ' + + 'tips on using these fields.', + }, + 'deviceCustomIPv6 Address1Label': { + key: 'c6a1Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomIPv6Address3: { + key: 'c6a3', + type: 'String', + len: null, + discription: 'One of four IPv6 address fields available to map ' + + 'fields that do not apply to any other in this dictionary. ' + + 'TIP: See the guidelines under “User-Defined Extensions” for ' + + 'tips on using these fields.', + }, + 'deviceCustomIPv6Address3 Label': { + key: 'c6a3Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + 'deviceCustomIPv6 Address4': { + key: 'c6a4', + type: 'String', + len: null, + discription: 'One of four IPv6 address fields available to map ' + + 'fields that do not apply to any other in this dictionary. ' + + 'TIP: See the guidelines under “User-Defined Extensions” for ' + + 'tips on using these fields.', + }, + 'deviceCustomIPv6 Address4Label': { + key: 'C6a4Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceEventCategory: { + key: 'cat', + type: 'String', + len: 1023, + discription: 'Represents the category assigned by the originating ' + + 'device. Devices often use their own categorization schema to ' + + 'classify event. Example: “/Monitor/Disk/Read”', + }, + deviceCustomFloatingPoint1: { + key: 'cfp1', + type: 'Number', + len: null, + discription: 'One of four floating point fields available to map ' + + 'fields that do not apply to any other in this dictionary.', + }, + 'deviceCustom FloatingPoint1Label': { + key: 'cfp1Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomFloatingPoint2: { + key: 'cfp2', + type: 'Number', + len: null, + discription: 'One of four floating point fields available to map ' + + 'fields that do not apply to any other in this dictionary.', + }, + 'deviceCustomFloatingPoint2 Label': { + key: 'cfp2Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomFloatingPoint3: { + key: 'cfp3', + type: 'Number', + len: null, + discription: 'One of four floating point fields available to map ' + + 'fields that do not apply to any other in this dictionary.', + }, + 'deviceCustom FloatingPoint3Label': { + key: 'cfp3Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomFloatingPoint4: { + key: 'cfp4', + type: 'Number', + len: null, + discription: 'One of four floating point fields available to map ' + + 'fields that do not apply to any other in this dictionary.', + }, + 'deviceCustom FloatingPoint4Label': { + key: 'cfp4Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomNumber1: { + key: 'cn1', + type: 'Number', + len: null, + discription: 'One of three number fields available to map fields ' + + 'that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific dictionary supplied field ' + + 'when possible.', + }, + deviceCustomNumber1Label: { + key: 'cn1Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + DeviceCustomNumber2: { + key: 'cn2', + type: 'Number', + len: null, + discription: 'One of three number fields available to map fields ' + + 'that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific, dictionary supplied field ' + + 'when possible.', + }, + deviceCustomNumber2Label: { + key: 'cn2Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomNumber3: { + key: 'cn3', + type: 'Number', + len: null, + discription: 'One of three number fields available to map fields ' + + 'that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific, dictionary supplied field ' + + 'when possible.', + }, + deviceCustomNumber3Label: { + key: 'cn3Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + baseEventCount: { + key: 'cnt', + type: 'Number', + len: null, + discription: 'A count associated with this event. How many times ' + + 'was this same event observed? Count can be omitted if it is 1.', + }, + deviceCustomString1: { + key: 'cs1', + type: 'String', + len: 4000, + discription: 'One of six strings available to map fields that do ' + + 'not apply to any other in this dictionary. Use sparingly and ' + + 'seek a more specific, dictionary supplied field when ' + + 'possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomString1Label: { + key: 'cs1Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomString2: { + key: 'cs2', + type: 'String', + len: 4000, + discription: 'One of six strings available to map fields that do ' + + 'not apply to any other in this dictionary. Use sparingly and ' + + 'seek a more specific, dictionary supplied field when ' + + 'possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomString2Label: { + key: 'cs2Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomString3: { + key: 'cs3', + type: 'String', + len: 4000, + discription: 'One of six strings available to map fields that do ' + + 'not apply to any other in this dictionary. Use sparingly and ' + + 'seek a more specific, dictionary supplied field when ' + + 'possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomString3Label: { + key: 'cs3Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomString4: { + key: 'cs4', + type: 'String', + len: 4000, + discription: 'One of six strings available to map fields that do ' + + 'not apply to any other in this dictionary. Use sparingly and ' + + 'seek a more specific, dictionary supplied field when ' + + 'possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomString4Label: { + key: 'cs4Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomString5: { + key: 'cs5', + type: 'String', + len: 4000, + discription: 'One of six strings available to map fields that do ' + + 'not apply to any other in this dictionary. Use sparingly and ' + + 'seek a more specific, dictionary supplied field when ' + + 'possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomString5Label: { + key: 'cs5Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomString6: { + key: 'cs6', + type: 'String', + len: 4000, + discription: 'One of six strings available to map fields that do ' + + 'not apply to any other in this dictionary. Use sparingly and ' + + 'seek a more specific, dictionary supplied field when ' + + 'possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomString6Label: { + key: 'cs6Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + destinationDnsDomain: { + key: 'destination DnsDomain', + type: 'String', + len: 255, + discription: 'The DNS domain part of the complete fully qualified ' + + 'domain name (FQDN).', + }, + destinationServiceName: { + key: 'destination ServiceName', + type: 'String', + len: 1023, + discription: 'The service targeted by this event. Example: “sshd”', + }, + 'destinationTranslated Address': { + key: 'Destination Translated Address', + type: 'String', + len: null, + discription: 'Identifies the translated destination that the event ' + + 'refers to in an IP network. The format is an IPv4 address. ' + + 'Example: “192.168.10.1”', + }, + destinationTranslatedPort: { + key: 'Destination TranslatedPort', + type: 'Number', + len: null, + discription: 'Port after it was translated; for example, a ' + + 'firewall. Valid port numbers are 0 to 65535.', + }, + deviceCustomDate1: { + key: 'deviceCustom Date1', + type: 'String', + len: null, + discription: 'One of two timestamp fields available to map fields ' + + 'that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific, dictionary supplied field ' + + 'when possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomDate1Label: { + key: 'deviceCustom Date1Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceCustomDate2: { + key: 'deviceCustom Date2', + type: 'String', + len: null, + discription: 'One of two timestamp fields available to map fields ' + + 'that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific, dictionary supplied field ' + + 'when possible. TIP: See the guidelines under “User-Defined ' + + 'Extensions” for tips on using these fields.', + }, + deviceCustomDate2Label: { + key: 'deviceCustom Date2Label', + type: 'String', + len: 1023, + discription: 'All custom fields have a corresponding label field. ' + + 'Each of these fields is a string and describes the purpose of ' + + 'the custom field.', + }, + deviceDirection: { + key: 'deviceDirection', + type: 'Number', + len: null, + discription: 'Any information about what direction the observed ' + + 'communication has taken. The following values are supported: ' + + '“0” for inbound or “1” for outbound', + }, + deviceDnsDomain: { + key: 'deviceDns Domain', + type: 'String', + len: 255, + discription: 'The DNS domain part of the complete fully qualified ' + + 'domain name (FQDN).', + }, + deviceExternalId: { + key: 'device ExternalId', + type: 'String', + len: 255, + discription: 'A name that uniquely identifies the device ' + + 'generating this event.', + }, + deviceFacility: { + key: 'deviceFacility', + type: 'String', + len: 1023, + discription: 'The facility generating this event. For example, ' + + 'Syslog has an explicit facility associated with every event.', + }, + deviceInboundInterface: { + key: 'deviceInbound Interface', + type: 'String', + len: 128, + discription: 'Interface on which the packet or data entered the ' + + 'device.', + }, + deviceNtDomain: { + key: 'deviceNt Domain', + type: 'String', + len: 255, + discription: 'The Windows domain name of the device address.', + }, + deviceOutboundInterface: { + key: 'Device Outbound Interface', + type: 'String', + len: 128, + discription: 'Interface on which the packet or data left the ' + + 'device.', + }, + devicePayloadId: { + key: 'Device PayloadId', + type: 'String', + len: 128, + discription: 'Unique identifier for the payload associated with ' + + 'the event.', + }, + deviceProcessName: { + key: 'deviceProcess Name', + type: 'String', + len: 1023, + discription: 'Process name associated with the event. An example ' + + 'might be the process generating the syslog entry in UNIX.', + }, + deviceTranslatedAddress: { + key: 'device Translated Address', + type: 'String', + len: null, + discription: 'Identifies the translated device address that the ' + + 'event refers to in an IP network. The format is an IPv4 ' + + 'address. Example: “192.168.10.1”', + }, + destinationHostName: { + key: 'dhost', + type: 'String', + len: 1023, + discription: 'Identifies the destination that an event refers to ' + + 'in an IP network. The format should be a fully qualified ' + + 'domain name (FQDN) associated with the destination node, when ' + + 'a node is available. Examples: “host.domain.com” or “host”.', + }, + destinationMacAddress: { + key: 'dmac', + type: 'String', + len: null, + discription: 'Six colon-seperated hexadecimal numbers. Example: ' + + '“00:0D:60:AF:1B:61”', + }, + destinationNtDomain: { + key: 'dntdom', + type: 'String', + len: 255, + discription: 'The Windows domain name of the destination address.', + }, + destinationProcessId: { + key: 'dpid', + type: 'Number', + len: null, + discription: 'Provides the ID of the destination process ' + + 'associated with the event. For example, if an event contains ' + + 'process ID 105, 105” is the process ID.', + }, + destinationUserPrivileges: { + key: 'dpriv', + type: 'String', + len: 1023, + discription: 'The typical values are “Administrator”, “User”, and ' + + '“Guest”. This identifies the destination user’s privileges. ' + + 'In UNIX, for example, activity executed on the root user ' + + 'would be identified with destinationUser Privileges of ' + + '“Administrator”.', + }, + destinationProcessName: { + key: 'dproc', + type: 'String', + len: 1023, + discription: 'The name of the event’s destination process. ' + + 'Example: “telnetd” or “sshd”.', + }, + destinationPort: { + key: 'dpt', + type: 'Number', + len: null, + discription: 'The valid port numbers are between 0 and 65535.', + }, + destinationAddress: { + key: 'dst', + type: 'String', + len: null, + discription: 'Identifies the destination address that the event ' + + 'refers to in an IP network. The format is an IPv4 address. ' + + 'Example: “192.168.10.1”', + }, + deviceTimeZone: { + key: 'dtz', + type: 'String', + len: 255, + discription: 'The timezone for the device generating the event.', + }, + destinationUserId: { + key: 'duid', + type: 'String', + len: 1023, + discription: 'Identifies the destination user by ID. For example, ' + + 'in UNIX, the root user is generally associated with user ' + + 'ID 0.', + }, + destinationUserName: { + key: 'duser', + type: 'String', + len: 1023, + discription: 'Identifies the destination user by name. This is the ' + + 'user associated with the event’s destination. Email addresses ' + + 'are often mapped into the UserName fields. The recipient is a ' + + 'candidate to put into this field.', + }, + deviceAddress: { + key: 'dvc', + type: 'String', + len: null, + discription: 'Identifies the device address that an event refers ' + + 'to in an IP network. The format is an IPv4 address. Example: ' + + '“192.168.10.1”.', + }, + deviceHostName: { + key: 'dvchost', + type: 'String', + len: 100, + discription: 'The format should be a fully qualified domain name ' + + '(FQDN) associated with the device node, when a node is ' + + 'available. Example: “host.domain.com” or “host”.', + }, + deviceMacAddress: { + key: 'dvcmac', + type: 'String', + len: null, + discription: 'Six colon-separated hexadecimal numbers. Example: ' + + '“00:0D:60:AF:1B:61”', + }, + deviceProcessId: { + key: 'dvcpid', + type: 'Number', + len: null, + discription: 'Provides the ID of the process on the device ' + + 'generating the event.', + }, + endTime: { + key: 'end', + type: 'String', + len: null, + discription: 'The time at which the activity related to the event ' + + 'ended. The format is MMM dd yyyy HH:mm:ss or milliseconds ' + + 'since epoch (Jan 1st1970). An example would be reporting the ' + + 'end of a session.', + }, + externalId: { + key: 'externalId', + type: 'String', + len: 40, + discription: 'The ID used by an originating device. They are ' + + 'usually increasing numbers, associated with events.', + }, + fileCreateTime: { + key: 'fileCreateTime', + type: 'String', + len: null, + discription: 'Time when the file was created.', + }, + fileHash: { + key: 'fileHash', + type: 'String', + len: 255, + discription: 'Hash of a file.', + }, + fileId: { + key: 'fileId', + type: 'String', + len: 1023, + discription: 'An ID associated with a file could be the inode.', + }, + fileModificationTime: { + key: 'fileModification Time', + type: 'String', + len: null, + discription: 'Time when the file was last modified.', + }, + filePath: { + key: 'filePath', + type: 'String', + len: 1023, + discription: 'Full path to the file, including file name itself. ' + + 'Example: C:\Program Files \WindowsNT\Accessories\ wordpad.exe ' + + 'or /usr/bin/zip', + }, + filePermission: { + key: 'filePermission', + type: 'String', + len: 1023, + discription: 'Permissions of the file.', + }, + fileType: { + key: 'fileType', + type: 'String', + len: 1023, + discription: 'Type of file (pipe, socket, etc.)', + }, + flexDate1: { + key: 'flexDate1', + type: 'String', + len: null, + discription: 'A timestamp field available to map a timestamp that ' + + 'does not apply to any other defined timestamp field in this ' + + 'dictionary. Use all flex fields sparingly and seek a more ' + + 'specific, dictionary supplied field when possible. These ' + + 'fields are typically reserved for customer use and should not ' + + 'be set by vendors unless necessary.', + }, + flexDate1Label: { + key: 'flexDate1Label', + type: 'String', + len: 128, + discription: 'The label field is a string and describes the ' + + 'purpose of the flex field.', + }, + flexString1: { + key: 'flexString1', + type: 'String', + len: 1023, + discription: 'One of four floating point fields available to map ' + + 'fields that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific, dictionary supplied field ' + + 'when possible. These fields are typically reserved for ' + + 'customer use and should not be set by vendors unless ' + + 'necessary.', + }, + flexString1Label: { + key: 'flexString1 Label', + type: 'String', + len: 128, + discription: 'The label field is a string and describes the ' + + 'purpose of the flex field.', + }, + flexString2: { + key: 'flexString2', + type: 'String', + len: 1023, + discription: 'One of four floating point fields available to map ' + + 'fields that do not apply to any other in this dictionary. Use ' + + 'sparingly and seek a more specific, dictionary supplied field ' + + 'when possible. These fields are typically reserved for ' + + 'customer use and should not be set by vendors unless ' + + 'necessary.', + }, + flexString2Label: { + key: 'flex String2Label', + type: 'String', + len: 128, + discription: 'The label field is a string and describes the ' + + 'purpose of the flex field.', + }, + filename: { + key: 'fname', + type: 'String', + len: 1023, + discription: 'Name of the file only (without its path).', + }, + fileSize: { + key: 'fsize', + type: 'Number', + len: null, + discription: 'Size of the file.', + }, + bytesIn: { + key: 'in', + type: 'Number', + len: null, + discription: 'Number of bytes transferred inbound, relative to the ' + + 'source to destination relationship, meaning that data was ' + + 'flowing from source to destination.', + }, + message: { + key: 'msg', + type: 'String', + len: 1023, + discription: 'An arbitrary message giving more details about the ' + + 'event. Multi-line entries can be produced by using \n as the ' + + 'new line separator.', + }, + oldFileCreateTime: { + key: 'oldFileCreate Time', + type: 'String', + len: null, + discription: 'Time when old file was created.', + }, + oldFileHash: { + key: 'oldFileHash', + type: 'String', + len: 255, + discription: 'Hash of the old file.', + }, + oldFileId: { + key: 'oldFileId', + type: 'String', + len: 1023, + discription: 'An ID associated with the old file could be the ' + + 'inode.', + }, + oldFileModificationTime: { + key: 'oldFile Modification Time', + type: 'String', + len: null, + discription: 'Time when old file was last modified.', + }, + oldFileName: { + key: 'oldFileName', + type: 'String', + len: 1023, + discription: 'Name of the old file.', + }, + oldFilePath: { + key: 'oldFilePath', + type: 'String', + len: 1023, + discription: 'Full path to the old fiWindowsNT\\Accessories le, ' + + 'including the file name itself. Examples: c:\\Program ' + + 'Files\\wordpad.exe or /usr/bin/zip', + }, + oldFileSize: { + key: 'oldFileSize', + type: 'Number', + len: null, + discription: 'Size of the old file.', + }, + oldFileType: { + key: 'oldFileType', + type: 'String', + len: 1023, + discription: 'Type of the old file (pipe, socket, etc.)', + }, + bytesOut: { + key: 'out', + type: 'Number', + len: null, + discription: 'Number of bytes transferred outbound relative to the ' + + 'source to destination relationship. For example, the byte ' + + 'number of data flowing from the destination to the source.', + }, + eventOutcome: { + key: 'outcome', + type: 'String', + len: 63, + discription: 'Displays the outcome, usually as ‘success’ or ' + + '‘failure’.', + }, + transportProtocol: { + key: 'proto', + type: 'String', + len: 31, + discription: 'Identifies the Layer-4 protocol used. The possible ' + + 'values are protocols such as TCP or UDP.', + }, + Reason: { + key: 'reason', + type: 'String', + len: 1023, + discription: 'The reason an audit event was generated. For ' + + 'example “badd password” or “unknown user”. This could also be ' + + 'an error or return code. Example: “0x1234”', + }, + requestUrl: { + key: 'request', + type: 'String', + len: 1023, + discription: 'In the case of an HTTP request, this field contains ' + + 'the URL accessed. The URL should contain the protocol as ' + + 'well. Example: “http://www/secure.com”', + }, + requestClientApplication: { + key: 'requestClient Application', + type: 'String', + len: 1023, + discription: 'The User-Agent associated with the request.', + }, + requestContext: { + key: 'requestContext', + type: 'String', + len: 2048, + discription: 'Description of the content from which the request ' + + 'originated (for example, HTTP Referrer)', + }, + requestCookies: { + key: 'requestCookies', + type: 'String', + len: 1023, + discription: 'Cookies associated with the request.', + }, + requestMethod: { + key: 'requestMethod', + type: 'String', + len: 1023, + discription: 'The method used to access a URL. Possible values: ' + + '“POST”, “GET”, etc.', + }, + deviceReceiptTime: { + key: 'rt', + type: 'String', + len: null, + discription: 'The time at which the event related to the activity ' + + 'was received. The format is MMM dd yyyy HH:mm:ss or ' + + 'milliseconds since epoch (Jan 1st 1970)', + }, + sourceHostName: { + key: 'shost', + type: 'String', + len: 1023, + discription: 'Identifies the source that an event refers to in an ' + + 'IP network. The format should be a fully qualified domain ' + + 'name (DQDN) associated with the source node, when a mode is ' + + 'available. Examples: “host” or “host.domain.com”.', + }, + sourceMacAddress: { + key: 'smac', + type: 'String', + len: null, + discription: 'Six colon-separated hexadecimal numbers. Example: ' + + '“00:0D:60:AF:1B:61”', + }, + sourceNtDomain: { + key: 'sntdom', + type: 'String', + len: 255, + discription: 'The Windows domain name for the source address.', + }, + sourceDnsDomain: { + key: 'sourceDns Domain', + type: 'String', + len: 255, + discription: 'The DNS domain part of the complete fully qualified ' + + 'domain name (FQDN).', + }, + sourceServiceName: { + key: 'source ServiceName', + type: 'String', + len: 1023, + discription: 'The service that is responsible for generating this ' + + 'event.', + }, + sourceTranslatedAddress: { + key: 'source Translated Address', + type: 'String', + len: null, + discription: 'Identifies the translated source that the event ' + + 'refers to in an IP network. The format is an IPv4 address. ' + + 'Example: “192.168.10.1”.', + }, + sourceTranslatedPort: { + key: 'source TranslatedPort', + type: 'Number', + len: null, + discription: 'A port number after being translated by, for ' + + 'example, a firewall. Valid port numbers are 0 to 65535.', + }, + sourceProcessId: { + key: 'spid', + type: 'Number', + len: null, + discription: 'The ID of the source process associated with the ' + + 'event.', + }, + sourceUserPrivileges: { + key: 'spriv', + type: 'String', + len: 1023, + discription: 'The typical values are “Administrator”, “User”, and ' + + '“Guest”. It identifies the source user’s privileges. In UNIX, ' + + 'for example, activity executed by the root user would be ' + + 'identified with “Administrator”.', + }, + sourceProcessName: { + key: 'sproc', + type: 'String', + len: 1023, + discription: 'The name of the event’s source process.', + }, + sourcePort: { + key: 'spt', + type: 'Number', + len: null, + discription: 'The valid port numbers are 0 to 65535.', + }, + sourceAddress: { + key: 'src', + type: 'String', + len: null, + discription: 'Identifies the source that an event refers to in an ' + + 'IP network. The format is an IPv4 address. Example: ' + + '“192.168.10.1”.', + }, + startTime: { + key: 'start', + type: 'String', + len: null, + discription: 'The time when the activity the event referred to ' + + 'started. The format is MMM dd yyyy HH:mm:ss or milliseconds ' + + 'since epoch (Jan 1st 1970)', + }, + sourceUserId: { + key: 'suid', + type: 'String', + len: 1023, + discription: 'Identifies the source user by ID. This is the user ' + + 'associated with the source of the event. For example, in ' + + 'UNIX, the root user is generally associated with user ID 0.', + }, + sourceUserName: { + key: 'suser', + type: 'String', + len: 1023, + discription: 'Identifies the source user by name. Email addresses ' + + 'are also mapped into the UserName fields. The sender is a ' + + 'candidate to put into this field.', + }, + type: { + key: 'type', + type: 'Number', + len: null, + discription: '0 means base event, 1 means aggregated, 2 means ' + + 'correlation, and 3 means action. This field can be omitted ' + + 'for base events (type 0).', + }, + agentDnsDomain: { + key: 'agentDns Domain', + type: 'String', + len: 255, + discription: 'The DNS domain name of the ArcSight connector that ' + + 'processed the event.', + }, + agentNtDomain: { + key: 'agentNtDomain', + type: 'String', + len: 255, + discription: '', + }, + agentTranslatedAddress: { + key: 'agentTranslated Address', + type: 'String', + len: null, + discription: '', + }, + 'agentTranslatedZone ExternalID': { + key: 'agentTranslated ZoneExternalID', + type: 'String', + len: 200, + discription: '', + }, + agentTranslatedZoneURI: { + key: 'agentTranslated Zone URI', + type: 'String', + len: 2048, + discription: '', + }, + agentZoneExternalID: { + key: 'agentZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + agentZoneURI: { + key: 'agentZoneURI', + type: 'String', + len: 2048, + discription: '', + }, + agentAddress: { + key: 'agt', + type: 'String', + len: null, + discription: 'The IP address of the ArcSight connector that ' + + 'processed the event.', + }, + agentHostName: { + key: 'ahost', + type: 'String', + len: 1023, + discription: 'The hostname of the ArcSight connector that ' + + 'processed the event.', + }, + agentId: { + key: 'aid', + type: 'String', + len: 40, + discription: 'The agent ID of the ArcSight connector that ' + + 'processed the event.', + }, + agentMacAddress: { + key: 'amac', + type: 'String', + len: null, + discription: 'The MAC address of the ArcSight connector that ' + + 'processed the event.', + }, + agentReceiptTime: { + key: 'art', + type: 'String', + len: null, + discription: 'The time at which information about the event was ' + + 'received by the ArcSight connector.', + }, + agentType: { + key: 'at', + type: 'String', + len: 63, + discription: 'The agent type of the ArcSight connector that ' + + 'processed the event', + }, + agentTimeZone: { + key: 'atz', + type: 'String', + len: 255, + discription: 'The agent time zone of the ArcSight connector that ' + + 'processed the event.', + }, + agentVersion: { + key: 'av', + type: 'String', + len: 31, + discription: 'The version of the ArcSight connector that processed ' + + 'the event.', + }, + customerExternalID: { + key: 'customer ExternalID', + type: 'String', + len: 200, + discription: '', + }, + customerURI: { + key: 'customerURI', + type: 'String', + len: 2048, + discription: '', + }, + 'destinationTranslated ZoneExternalID': { + key: 'destination TranslatedZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + 'destinationTranslated ZoneURI': { + key: 'destination Translated ZoneURI', + type: 'String', + len: 2048, + discription: 'The URI for the Translated Zone that the destination ' + + 'asset has been assigned to in ArcSight.', + }, + destinationZoneExternalID: { + key: 'destinationZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + destinationZoneURI: { + key: 'destinationZone URI', + type: 'String', + len: 2048, + discription: 'The URI for the Zone that the destination asset has ' + + 'been assigned to in ArcSight.', + }, + 'deviceTranslatedZone ExternalID': { + key: 'device TranslatedZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + deviceTranslatedZoneURI: { + key: 'device TranslatedZone URI', + type: 'String', + len: 2048, + discription: 'The URI for the Translated Zone that the device ' + + 'asset has been assigned to in ArcSight.', + }, + deviceZoneExternalID: { + key: 'deviceZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + deviceZoneURI: { + key: 'deviceZoneURI', + type: 'String', + len: 2048, + discription: 'Thee URI for the Zone that the device asset has been ' + + 'assigned to in ArcSight.', + }, + destinationGeoLatitude: { + key: 'dlat', + type: 'Number', + len: null, + discription: 'The latitudinal value from which the ' + + 'destination’s IP address belongs.', + }, + destinationGeoLongitude: { + key: 'dlong', + type: 'Number', + len: null, + discription: 'The longitudinal value from which the destination’s ' + + 'IP address belongs.', + }, + eventId: { + key: 'eventId', + type: 'Number', + len: null, + discription: 'This is a unique ID that ArcSight assigns to each ' + + 'event.', + }, + rawEvent: { + key: 'rawEvent', + type: 'String', + len: 4000, + discription: '', + }, + sourceGeoLatitude: { + key: 'slat', + type: 'Number', + len: null, + discription: '', + }, + sourceGeoLongitude: { + key: 'slong', + type: 'Number', + len: null, + discription: '', + }, + 'sourceTranslatedZone ExternalID': { + key: 'source TranslatedZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + sourceTranslatedZoneURI: { + key: 'source TranslatedZone URI', + type: 'String', + len: 2048, + discription: 'The URI for the Translated Zone that the destination ' + + 'asset has been assigned to in ArcSight.', + }, + sourceZoneExternalID: { + key: 'sourceZone ExternalID', + type: 'String', + len: 200, + discription: '', + }, + sourceZoneURI: { + key: 'sourceZoneURI', + type: 'String', + len: 2048, + discription: 'The URI for the Zone that the source asset has been ' + + 'assigned to in ArcSight.' + }, + }; + if (typeof this.deviceVendor !== 'string' + || typeof this.deviceProduct !== 'string' + || typeof this.deviceVersion !== 'string') { + throw new Error('TYPE ERROR: CEF Device Info must be a string'); + } + if (this.severity + && ((typeof this.severity === 'string' + && (this.severity !== 'Unknown' + && this.severity !== 'Low' + && this.severity !== 'Medium' + && this.severity !== 'High' + && this.severity !== 'Very-High')) + || (typeof this.severity === 'number' + && (this.severity < 0 + || this.severity > 10)))) { + throw new Error('TYPE ERROR: CEF Severity not set correctly'); + } + const cefExts = Object.entries(this.extensions); + const cefExtsLen = cefExts.length; + for (let ext = 0; ext < cefExtsLen; ext++) { + if (cefExts[ext][1] !== null) { + if (Extensions[cefExts[ext][0]]) { + if (typeof cefExts[ext][1] === Extensions[cefExts[ext][0]] + .type + .toLowerCase()) { + if (Extensions[cefExts[ext][0]].len > 0 + && typeof cefExts[ext][1] === 'string' + && cefExts[ext][1].length + > Extensions[cefExts[ext][0]].len) { + let errMsg = 'FORMAT ERROR:'; + errMsg += ' CEF Extention Key'; + errMsg += ' ' + cefExts[ext][0]; + errMsg += ' value length is to long;'; + errMsg += ' max length is'; + errMsg += ' ' + Extensions[cefExts[ext][0]].len; + throw new Error(errMsg); + } + } + else { + let errMsg = 'TYPE ERROR:'; + errMsg += ' CEF Key'; + errMsg += ' ' + cefExts[ext][0]; + errMsg += ' value type was expected to be'; + errMsg += ' ' + Extensions[cefExts[ext][0]].type.toLowerCase(); + throw new Error(errMsg); + } + } + } + } + return true; + } + /** + * Build a CEF formated string + * @public + * @return {string} - String with formated message + */ + buildMessage() { + let fmtMsg = 'CEF:0'; + fmtMsg += '|' + this.deviceVendor; + fmtMsg += '|' + this.deviceProduct; + fmtMsg += '|' + this.deviceVersion; + fmtMsg += '|' + this.deviceEventClassId; + fmtMsg += '|' + this.name; + fmtMsg += '|' + this.severity; + fmtMsg += '|'; + const cefExts = Object.entries(this.extensions); + const cefExtsLen = cefExts.length; + for (let ext = 0; ext < cefExtsLen; ext++) { + if (cefExts[ext][1] !== null) { + fmtMsg += cefExts[ext][0] + '=' + cefExts[ext][1] + ' '; + } + } + return fmtMsg; + } + /** + * @public + */ + async send() { + const result = this.buildMessage(); + return this.server.send(result); + } +} +exports.CEF = CEF; +exports.RgbToAnsi = rgbToAnsi; diff --git a/package.json b/package.json index 5be900c..42b4074 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@zeit/syslog-pro", - "version": "0.2.9", + "version": "0.2.9-misskey.2", "description": "A Syslog client which options for UDP, TCP, and TLS transport and suport for both RFC-3164 and RFC-5424 including Structured Data.", "keywords": [ "Syslog",