diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..4306d39 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,31 @@ +version: 2 +jobs: + test: + docker: + - image: circleci/node:14-stretch + steps: + - checkout + - restore_cache: + key: dependency-cache-{{ checksum "package.json" }} + - run: + name: Audit Dependencies + command: npm audit --audit-level=high + - run: + name: Installing Dependencies + command: npm install + - save_cache: + key: dependency-cache-{{ checksum "package.json" }} + paths: + - node_modules + - run: + name: Running Unit Tests + command: npm test + - run: + name: Running Integration Tests + command: npm run integration-test + +workflows: + version: 2 + build_and_test: + jobs: + - test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eb6e875..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: node_js -node_js: - - v14 - - v13 - - v12 -script: npm test && npm run integration-test diff --git a/CHANGELOG.md b/CHANGELOG.md index 470d8c9..d56e1c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ + ## 1.1.3 (November 20, 2020) + + * Update Sailor version to 2.6.18 + * Annual audit of the component code to check if it exposes a sensitive data in the logs + * Annual npm vulnerabilities audit + ## 1.1.2 (July 24, 2020) * Update sailor version to 2.6.14 diff --git a/README.md b/README.md index 10313ad..ae1aada 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# mssql-component [![NPM version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Dependency Status][daviddm-image]][daviddm-url] +[![CircleCI](https://circleci.com/gh/elasticio/mssql-component.svg?style=svg)](https://circleci.com/gh/elasticio/mssql-component) +# mssql-component > elastic.io integration component for Microsoft SQL Server # mssql-component diff --git a/lib/actions/insert.js b/lib/actions/insert.js index 85bc719..b636de6 100644 --- a/lib/actions/insert.js +++ b/lib/actions/insert.js @@ -26,18 +26,18 @@ function init(cfg) { cfg.database }${(cfg.domain) ? `?domain=${cfg.domain}&encrypt=${cfg.encrypt}` : `?encrypt=${cfg.encrypt}`}`; - logger.trace(conString); + logger.debug('Connection string is created'); return co(function* gen() { logger.info('Connecting to the database'); const connection = new cosql.Connection(conString); // Always attach an error listener - connection.on('error', (err) => this.emit('error', err)); + connection.on('error', err => this.emit('error', err)); let sql = cfg.query; yield connection.connect(); logger.info('Connection established'); - logger.trace('Preparing query=%s', sql); + logger.debug('Preparing query...'); const vars = sql.match(VARS_REGEXP); - logger.trace('Found following prepared variable:type pairs=%j', vars); + logger.debug('Found prepared variable:type pairs'); pstmt = new cosql.PreparedStatement(connection); for (const tuple of vars) { const [placeholder, type] = tuple.split(':'); @@ -74,7 +74,7 @@ function init(cfg) { // Now let's remove all :string :boolean :date etc to the name only sql = sql.replace(tuple, placeholder); } - logger.trace('Resulting SQL=%s', sql); + logger.trace('Resulting SQL is ready'); yield pstmt.prepare(sql); logger.info('Preparing statement created'); }.bind(this)); diff --git a/lib/actions/select.js b/lib/actions/select.js index bb1bcf2..8c9e917 100644 --- a/lib/actions/select.js +++ b/lib/actions/select.js @@ -28,7 +28,7 @@ function init(cfg) { return co(function* gen() { logger.info('Connecting to the database'); connection = new cosql.Connection(conString); - connection.on('error', (err) => this.emit('error', err)); + connection.on('error', err => this.emit('error', err)); yield connection.connect(); logger.info('Connection established'); }.bind(this)); @@ -47,15 +47,14 @@ function processAction(msg, cfg, snapshot = {}) { const lastPoll = snapshot.lastPoll || new Date(0).toISOString(); this.logger.info('Last polling timestamp=%s', lastPoll); const sql = originalSql.split(LAST_POLL_PLACEHOLDER).join(lastPoll); - this.logger.trace('Original query=%s', originalSql); - this.logger.trace('Transformed query=%s', sql); + this.logger.debug('Transformed query is ready'); const that = this; return co(function* gen() { const request = new cosql.Request(connection); request.stream = true; - request.on('recordset', (recordset) => { - that.logger.trace('Have got recordset metadata=%j', recordset); + request.on('recordset', () => { + that.logger.trace('Have got recordset metadata'); }); request.on('row', (row) => { diff --git a/package-lock.json b/package-lock.json index a273c55..e3a0b68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mssql-component", - "version": "1.1.2", + "version": "1.1.3", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -353,9 +353,9 @@ } }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -749,9 +749,9 @@ }, "dependencies": { "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -765,11 +765,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -778,6 +778,11 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -847,9 +852,9 @@ } }, "elasticio-sailor-nodejs": { - "version": "2.6.14", - "resolved": "https://registry.npmjs.org/elasticio-sailor-nodejs/-/elasticio-sailor-nodejs-2.6.14.tgz", - "integrity": "sha512-7hMSpSYOD+uN0ZUiUvAXDxAfn979CBfu59IC76aL1IYyj82YXyz3GI3n4/1ssyK2PWSp765KZ1FiquiAd2BWBw==", + "version": "2.6.18", + "resolved": "https://registry.npmjs.org/elasticio-sailor-nodejs/-/elasticio-sailor-nodejs-2.6.18.tgz", + "integrity": "sha512-ZtGI3p/jzmpRz4A9GWhj6yocXRJq4ixz2j4a898XKVUynJxL68tAeqWdcui3UxD6O9gvTDOdxkrVg289UxjZkw==", "requires": { "@elastic.io/object-storage-client": "0.0.2-dev", "amqplib": "0.5.1", @@ -876,6 +881,11 @@ "mv": "~2", "safe-json-stringify": "~1" } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" } } }, @@ -1638,9 +1648,9 @@ "dev": true }, "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" }, "is-callable": { "version": "1.2.0", @@ -1861,9 +1871,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash.get": { "version": "4.4.2", diff --git a/package.json b/package.json index 1cd5d20..7b44e8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mssql-component", - "version": "1.1.2", + "version": "1.1.3", "description": "elastic.io integration component for Microsoft SQL Server", "homepage": "https://www.elastic.io", "author": { @@ -25,16 +25,16 @@ "node": ">=12.13.0" }, "scripts": { - "pretest": "node_modules/.bin/eslint lib spec spec-integration --ext .json --ext .js --fix", + "pretest": "eslint lib spec spec-integration --ext .json --ext .js --fix", "test": "NODE_ENV=test mocha spec/*", - "integration-test": "NODE_ENV=test mocha spec-integration/* --exit" + "integration-test": "mocha spec-integration --recursive --timeout 10000 --exit" }, "dependencies": { "bluebird": "3.4.6", "co": "4.6.0", "co-mssql": "1.3.0", "elasticio-node": "0.0.9", - "elasticio-sailor-nodejs": "2.6.14", + "elasticio-sailor-nodejs": "2.6.18", "@elastic.io/component-logger": "0.0.1", "mssql": "4.1.0", "request": "2.87.0", diff --git a/spec-integration/integration.spec.js b/spec-integration/integration.spec.js index 192e57f..b50a056 100644 --- a/spec-integration/integration.spec.js +++ b/spec-integration/integration.spec.js @@ -92,7 +92,7 @@ describe('Integration test', () => { select.process.call({ emit: emitter, logger, - }, msg, cfg).catch((err) => done(err)); + }, msg, cfg).catch(err => done(err)); }); }); @@ -124,7 +124,7 @@ describe('Integration test', () => { select.process.call({ emit: emitter, logger, - }, msg, cfg).catch((err) => done(err)); + }, msg, cfg).catch(err => done(err)); }); }); @@ -158,7 +158,7 @@ describe('Integration test', () => { select.process.call({ emit: emitter, logger, - }, msg, cfg, {}).catch((err) => done(err)); + }, msg, cfg, {}).catch(err => done(err)); }); }); }); diff --git a/spec-integration/verifyCredentials.spec.js b/spec-integration/verifyCredentials.spec.js new file mode 100644 index 0000000..a5ddf24 --- /dev/null +++ b/spec-integration/verifyCredentials.spec.js @@ -0,0 +1,37 @@ +const fs = require('fs'); +const { expect } = require('chai'); +const logger = require('@elastic.io/component-logger')(); +const verifyCredentials = require('../verifyCredentials'); + +describe('Integration test verify credentials', () => { + if (fs.existsSync('.env')) { + // eslint-disable-next-line global-require + require('dotenv').config(); + } + before(() => { + if (!process.env.MSSQL_USERNAME) { throw new Error('Please set MSSQL_USERNAME env variable to proceed'); } + if (!process.env.MSSQL_PASSWORD) { throw new Error('Please set MSSQL_PASSWORD env variable to proceed'); } + if (!process.env.MSSQL_SERVER) { throw new Error('Please set MSSQL_SERVER env variable to proceed'); } + if (!process.env.MSSQL_DATABASE) { throw new Error('Please set MSSQL_DATABASE env variable to proceed'); } + }); + const cfg = { + username: process.env.MSSQL_USERNAME, + password: process.env.MSSQL_PASSWORD, + server: process.env.MSSQL_SERVER, + port: process.env.MSSQL_PORT, + instance: process.env.MSSQL_INSTANCE, + database: process.env.MSSQL_DATABASE, + domain: process.env.MSSQL_DOMAIN, + encrypt: process.env.MSSQL_ENCRYPT, + }; + + it('should successfully verify credentials', (done) => { + verifyCredentials.call({ logger }, cfg, (err, result) => { + if (err) { + done(err); + } + expect(result).deep.equal({ verified: true }); + done(); + }); + }); +}); diff --git a/verifyCredentials.js b/verifyCredentials.js index e924a0c..8fa979d 100644 --- a/verifyCredentials.js +++ b/verifyCredentials.js @@ -1,31 +1,31 @@ -'use strict'; const co = require('co'); const sql = require('co-mssql'); // This function will be called by the platform to verify credentials module.exports = function verifyCredentials(credentials, cb) { - console.log('Credentials passed for verification %j', credentials); - co(function*() { - console.log('Connecting to the database'); - var uri = 'mssql://' - + encodeURIComponent(credentials.username) - + ':' - + encodeURIComponent(credentials.password) - + '@' - + credentials.server - + ((credentials.port) ? ':' + credentials.port : '') - + ((credentials.instance) ? '/' + credentials.instance : '') - + '/' - + credentials.database - + ((credentials.domain) ? '?domain=' + credentials.domain + '&encrypt=' + credentials.encrypt - : '?encrypt=' + credentials.encrypt); - var connection = new sql.Connection(uri); + const self = this; + self.logger.info('Starting credentials verification'); + co(function* () { + self.logger.info('Connecting to the database'); + const uri = `mssql://${ + encodeURIComponent(credentials.username) + }:${ + encodeURIComponent(credentials.password) + }@${ + credentials.server + }${(credentials.port) ? `:${credentials.port}` : '' + }${(credentials.instance) ? `/${credentials.instance}` : '' + }/${ + credentials.database + }${(credentials.domain) ? `?domain=${credentials.domain}&encrypt=${credentials.encrypt}` + : `?encrypt=${credentials.encrypt}`}`; + const connection = new sql.Connection(uri); yield connection.connect(); - console.log('Verification completed successfully'); + self.logger.info('Verification completed successfully'); yield connection.close(); - cb(null, {verified: true}); - }).catch(err => { - console.log('Error occurred', err.stack || err); - cb(err , {verified: false}); + cb(null, { verified: true }); + }).catch((err) => { + self.logger.info('Error occurred, credentials are not valid'); + cb(err, { verified: false }); }); };