From b8ec1e228ee8d1b626615de07583dd6283a99450 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 13 Mar 2021 02:40:29 +0000 Subject: [PATCH 1/9] chore(deps): bump xmldom from 0.3.0 to 0.5.0 Bumps [xmldom](https://github.com/xmldom/xmldom) from 0.3.0 to 0.5.0. - [Release notes](https://github.com/xmldom/xmldom/releases) - [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md) - [Commits](https://github.com/xmldom/xmldom/compare/0.3.0...0.5.0) Signed-off-by: dependabot[bot] --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8644be4..1b15967 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ebics-client", - "version": "0.1.6", + "version": "0.1.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4367,9 +4367,9 @@ "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==" }, "xmldom": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz", - "integrity": "sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz", + "integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==" }, "xpath": { "version": "0.0.27", diff --git a/package.json b/package.json index 4e757de..839a572 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "request": "^2.88.2", "uuid": "^8.0.0", "xml-crypto": "^2.0.0", - "xmldom": "^0.3.0", + "xmldom": "^0.5.0", "xpath": "0.0.27" }, "devDependencies": { From a628e00b9baa286992acb8bbd0ae67e224e0bd4f Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Wed, 24 Mar 2021 22:13:18 +0100 Subject: [PATCH 2/9] Add support for Bank GPB AO (Gazprombank Russia) Add custom order types (G02, G52, G53, G1R and G1V) and examples for Bank GPB AO (Gazprombank Russia). Pain.001.001.06 (RUB) - G1R Pain.001.001.06 (FCY/FX/RLS) - G1V Pain.002.001.06 - G02 Camt.052.001.05 - G52 Camt.053.001.05 - G53 Signed-off-by: Herman van Hazendonk --- README.md | 1 + examples/send-g02-order-zipped.js | 28 ++++++++++++++++++++++++++++ examples/send-g1r-order.js | 20 ++++++++++++++++++++ examples/send-g1v-order.js | 20 ++++++++++++++++++++ examples/send-g52-order-zipped.js | 28 ++++++++++++++++++++++++++++ examples/send-g53-order-zipped.js | 28 ++++++++++++++++++++++++++++ lib/predefinedOrders/G02.js | 13 +++++++++++++ lib/predefinedOrders/G1R.js | 8 ++++++++ lib/predefinedOrders/G1V.js | 8 ++++++++ lib/predefinedOrders/G52.js | 13 +++++++++++++ lib/predefinedOrders/G53.js | 13 +++++++++++++ lib/predefinedOrders/index.js | 10 ++++++++++ 12 files changed, 190 insertions(+) create mode 100644 examples/send-g02-order-zipped.js create mode 100644 examples/send-g1r-order.js create mode 100644 examples/send-g1v-order.js create mode 100644 examples/send-g52-order-zipped.js create mode 100644 examples/send-g53-order-zipped.js create mode 100644 lib/predefinedOrders/G02.js create mode 100644 lib/predefinedOrders/G1R.js create mode 100644 lib/predefinedOrders/G1V.js create mode 100644 lib/predefinedOrders/G52.js create mode 100644 lib/predefinedOrders/G53.js diff --git a/README.md b/README.md index 4ca07c8..b14d96d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ The client is currently tested and verified to work with the following banks: * [Raiffeisen Schweiz](https://www.raiffeisen.ch/rch/de.html) * [BW Bank](https://www.bw-bank.de/de/home.html) * [Bank GPB International S.A.](https://gazprombank.lu/e-banking) +* [Bank GPB AO](https://gazprombank.ru/) ## Inspiration diff --git a/examples/send-g02-order-zipped.js b/examples/send-g02-order-zipped.js new file mode 100644 index 0000000..6026707 --- /dev/null +++ b/examples/send-g02-order-zipped.js @@ -0,0 +1,28 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +client.send(Orders.G02(null, null)) // startDate 'YYYY-MM-DD', endDate 'YYYY-MM-DD' + .then((resp) => { + console.log('Response for G02 order %j', resp); + if (resp.technicalCode !== '000000') + throw new Error('Something went wrong'); + + // Parsing and processing the G02 Pain.002.001.06 file should happen somewhere here, ideally after saving it to disk + const data = Buffer.from(resp.orderData); + let distPath = "G02.zip"; + const dstZip = fs.createWriteStream(distPath); + dstZip.write(data); + dstZip.end(); + }) + + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/examples/send-g1r-order.js b/examples/send-g1r-order.js new file mode 100644 index 0000000..37d560b --- /dev/null +++ b/examples/send-g1r-order.js @@ -0,0 +1,20 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +const paymentFile = fs.readFileSync('RUB_PAYMENT.xml').toString(); + +client.send(Orders.G1R(paymentFile)) + .then((resp) => { + console.log('Response for G1R order %j', resp); + }) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/examples/send-g1v-order.js b/examples/send-g1v-order.js new file mode 100644 index 0000000..a5d7392 --- /dev/null +++ b/examples/send-g1v-order.js @@ -0,0 +1,20 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +const paymentFile = fs.readFileSync('FCY_PAYMENT.xml').toString(); + +client.send(Orders.G1V(paymentFile)) + .then((resp) => { + console.log('Response for G1V order %j', resp); + }) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/examples/send-g52-order-zipped.js b/examples/send-g52-order-zipped.js new file mode 100644 index 0000000..99c8e1f --- /dev/null +++ b/examples/send-g52-order-zipped.js @@ -0,0 +1,28 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +client.send(Orders.G52(null, null)) // startDate 'YYYY-MM-DD', endDate 'YYYY-MM-DD' + .then((resp) => { + console.log('Response for G52 order %j', resp); + if (resp.technicalCode !== '000000') + throw new Error('Something went wrong'); + + // Parsing and processing the CAMT052 file should happen somewhere here, ideally after saving it to disk + const data = Buffer.from(resp.orderData); + let distPath = "CAMT052-G52.zip"; + const dstZip = fs.createWriteStream(distPath); + dstZip.write(data); + dstZip.end(); + }) + + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/examples/send-g53-order-zipped.js b/examples/send-g53-order-zipped.js new file mode 100644 index 0000000..5f43834 --- /dev/null +++ b/examples/send-g53-order-zipped.js @@ -0,0 +1,28 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +client.send(Orders.G53(null, null)) // startDate 'YYYY-MM-DD', endDate 'YYYY-MM-DD' + .then((resp) => { + console.log('Response for G53 order %j', resp); + if (resp.technicalCode !== '000000') + throw new Error('Something went wrong'); + + // Parsing and processing the CAMT053 file should happen somewhere here, ideally after saving it to disk + const data = Buffer.from(resp.orderData); + let distPath = "CAMT053-G53.zip"; + const dstZip = fs.createWriteStream(distPath); + dstZip.write(data); + dstZip.end(); + }) + + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/lib/predefinedOrders/G02.js b/lib/predefinedOrders/G02.js new file mode 100644 index 0000000..8ae0d65 --- /dev/null +++ b/lib/predefinedOrders/G02.js @@ -0,0 +1,13 @@ +'use strict'; + +const utils = require('../utils'); + +module.exports = (start = null, end = null) => ({ + version: 'h004', + orderDetails: { + OrderType: 'G02', + OrderAttribute: 'DZHNN', + StandardOrderParams: utils.dateRange(start, end), + }, + operation: 'download', +}); diff --git a/lib/predefinedOrders/G1R.js b/lib/predefinedOrders/G1R.js new file mode 100644 index 0000000..0882e20 --- /dev/null +++ b/lib/predefinedOrders/G1R.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = document => ({ + version: 'h004', + orderDetails: { OrderType: 'G1R', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + operation: 'upload', + document, +}); diff --git a/lib/predefinedOrders/G1V.js b/lib/predefinedOrders/G1V.js new file mode 100644 index 0000000..95e80cd --- /dev/null +++ b/lib/predefinedOrders/G1V.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = document => ({ + version: 'h004', + orderDetails: { OrderType: 'G1V', OrderAttribute: 'DZHNN', StandardOrderParams: {} }, + operation: 'upload', + document, +}); diff --git a/lib/predefinedOrders/G52.js b/lib/predefinedOrders/G52.js new file mode 100644 index 0000000..249bfa3 --- /dev/null +++ b/lib/predefinedOrders/G52.js @@ -0,0 +1,13 @@ +'use strict'; + +const utils = require('../utils'); + +module.exports = (start = null, end = null) => ({ + version: 'h004', + orderDetails: { + OrderType: 'G52', + OrderAttribute: 'DZHNN', + StandardOrderParams: utils.dateRange(start, end), + }, + operation: 'download', +}); diff --git a/lib/predefinedOrders/G53.js b/lib/predefinedOrders/G53.js new file mode 100644 index 0000000..ba09297 --- /dev/null +++ b/lib/predefinedOrders/G53.js @@ -0,0 +1,13 @@ +'use strict'; + +const utils = require('../utils'); + +module.exports = (start = null, end = null) => ({ + version: 'h004', + orderDetails: { + OrderType: 'G53', + OrderAttribute: 'DZHNN', + StandardOrderParams: utils.dateRange(start, end), + }, + operation: 'download', +}); diff --git a/lib/predefinedOrders/index.js b/lib/predefinedOrders/index.js index e32c57a..a5a1b43 100644 --- a/lib/predefinedOrders/index.js +++ b/lib/predefinedOrders/index.js @@ -14,6 +14,8 @@ const CCS = require('./CCS'); const XE3 = require('./XE3'); const XCT = require('./XCT'); const XG1 = require('./XG1'); +const G1V = require('./G1V'); +const G1R = require('./G1R'); const STA = require('./STA'); const VMK = require('./VMK'); @@ -27,6 +29,9 @@ const Z53 = require('./Z53'); const DKI = require('./DKI'); const C52 = require('./C52'); const C53 = require('./C53'); +const G52 = require('./G52'); +const G53 = require('./G53'); +const G02 = require('./G02'); module.exports = { INI, @@ -44,6 +49,8 @@ module.exports = { XE3, XCT, XG1, + G1V, + G1R, STA, VMK, @@ -56,4 +63,7 @@ module.exports = { DKI, C52, C53, + G52, + G53, + G02, }; From 8af3cb50c5b5b4d31e270c904f0e0baba2ee271d Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Wed, 24 Mar 2021 22:14:21 +0100 Subject: [PATCH 3/9] Add sample code for zipped CAMT statements Some banks provide zipped CAMT statements, add some example code on how to write these to a file. Signed-off-by: Herman van Hazendonk --- examples/send-c52-order-zipped.js | 28 ++++++++++++++++++++++++++++ examples/send-c53-order-zipped.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 examples/send-c52-order-zipped.js create mode 100644 examples/send-c53-order-zipped.js diff --git a/examples/send-c52-order-zipped.js b/examples/send-c52-order-zipped.js new file mode 100644 index 0000000..1ff6922 --- /dev/null +++ b/examples/send-c52-order-zipped.js @@ -0,0 +1,28 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +client.send(Orders.C52(null, null)) // startDate 'YYYY-MM-DD', endDate 'YYYY-MM-DD' + .then((resp) => { + console.log('Response for C52 order %j', resp); + if (resp.technicalCode !== '000000') + throw new Error('Something went wrong'); + + // Parsing and processing the CAMT052 file should happen somewhere here, ideally after saving it to disk + const data = Buffer.from(resp.orderData); + let distPath = "CAMT052.zip"; + const dstZip = fs.createWriteStream(distPath); + dstZip.write(data); + dstZip.end(); + }) + + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/examples/send-c53-order-zipped.js b/examples/send-c53-order-zipped.js new file mode 100644 index 0000000..cf9b432 --- /dev/null +++ b/examples/send-c53-order-zipped.js @@ -0,0 +1,28 @@ +#! /usr/bin/env node + +'use strict'; + +const fs = require('fs'); + +const client = require('./getClient')(); +const { Orders } = require('../index'); + +// The bank keys must have been already saved +client.send(Orders.C53(null, null)) // startDate 'YYYY-MM-DD', endDate 'YYYY-MM-DD' + .then((resp) => { + console.log('Response for C53 order %j', resp); + if (resp.technicalCode !== '000000') + throw new Error('Something went wrong'); + + // Parsing and processing the CAMT053 file should happen somewhere here, ideally after saving it to disk + const data = Buffer.from(resp.orderData); + let distPath = "CAMT053.zip"; + const dstZip = fs.createWriteStream(distPath); + dstZip.write(data); + dstZip.end(); + }) + + .catch((err) => { + console.error(err); + process.exit(1); + }); From 61581d1af753446380ad8c2d735867b844e2109f Mon Sep 17 00:00:00 2001 From: Herrie Date: Wed, 17 Mar 2021 11:13:39 +0100 Subject: [PATCH 4/9] Cryto.js: Nasty workaround for incorrect signature (257 vs 256) and hex key length (514 vs 512) For some unknown reason, the signature gets a length of 257 bytes instead of 256 bytes, and the length of the hex value is 514 bytes instead of 512 bytes. This works around it, until a proper fix is implemented. The bug seems to be caused by https://github.com/node-ebics/node-ebics-client/blob/master/lib/crypto/Crypto.js#L71 somehow. Signed-off-by: Herman van Hazendonk --- lib/crypto/Crypto.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/crypto/Crypto.js b/lib/crypto/Crypto.js index 445088c..a2032b5 100644 --- a/lib/crypto/Crypto.js +++ b/lib/crypto/Crypto.js @@ -72,7 +72,19 @@ module.exports = class Crypto { const power = new BigNumber(key.d()); const mod = new BigNumber(key.n()); - return (modPow(base, power, mod)).toBEBuffer().toString('base64'); + //Somehow sometimes we have a 514 byte hex key that starts with "00". In that case use only the last 512 bytes + var hexString = modPow(base, power, mod).toBEBuffer().toString('hex'); + if(hexString.substr(0,2) === "00" && hexString.length == 514) + { + console.log("hex string of key starts with \"00\" and is 514 bytes long, fixing it to be 512 bytes long by stripping leading \"00\""); + hexString = hexString.substr(2); + var base64String = Buffer.from(hexString, 'hex').toString('base64') + return base64String; + } + else + { + return (modPow(base, power, mod)).toBEBuffer().toString('base64'); + } } static pad(d) { From de92265c95f49389c161c3dc81c124210eb727d4 Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Thu, 25 Mar 2021 13:52:17 +0100 Subject: [PATCH 5/9] Examples: Improve config implementation for multibank. When using the software with multiple banks, the current config file solution wasn't very flexible. We had some different local implementation at our end. In order to use upstream software directly without any changes, suggesting to merge these changes. For me locally it would mean I can get rid of a lot of local example code which are currently bank and even entity specific and can be made more generic. This will also allow multiple EBICS connections with the same bank for different entities as well, in case this is needed. In our case we have multiple EBICS connections with the same bank. Signed-off-by: Herman van Hazendonk --- ...ion.json => config.production.testbank.json} | 0 .../config.production.testbank.testentity.json | 10 ++++++++++ examples/loadConfig.js | 17 ++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) rename examples/config/{config.production.json => config.production.testbank.json} (100%) create mode 100644 examples/config/config.production.testbank.testentity.json diff --git a/examples/config/config.production.json b/examples/config/config.production.testbank.json similarity index 100% rename from examples/config/config.production.json rename to examples/config/config.production.testbank.json diff --git a/examples/config/config.production.testbank.testentity.json b/examples/config/config.production.testbank.testentity.json new file mode 100644 index 0000000..1bb31fa --- /dev/null +++ b/examples/config/config.production.testbank.testentity.json @@ -0,0 +1,10 @@ +{ + "url": "https://ebics.server", + "partnerId": "EBICS ParnerID Production", + "userId": "MyUserIdProduction", + "hostId": "MyHostIdProduction", + "passphrase": "MyPasswordProduction", + "keyStoragePath": "./keys-prod", + "bankName":"Production Bank", + "languageCode":"en" +} diff --git a/examples/loadConfig.js b/examples/loadConfig.js index 7a1892c..09af4ed 100644 --- a/examples/loadConfig.js +++ b/examples/loadConfig.js @@ -20,11 +20,22 @@ const getDefaultEnv = () => { return parArg || process.env.NODE_ENV; } -const loadConfig = (configDirectory = path.join(__dirname, './config'), env = getDefaultEnv()) => { - console.log(`Loading config form ${configDirectory} with env set to ${env}.`); +const getBankIdentifier = () => { + const [,,,parArg] = process.argv; + return parArg || "testbank"; +} +const getEntityIdentifier = () => { + const [,,,,parArg] = process.argv; + return parArg || "" +} + +const loadConfig = (configDirectory = path.join(__dirname, './config'), env = getDefaultEnv(), bank = getBankIdentifier(), entity = getEntityIdentifier()) => { + entity ? console.log(`Loading config from ${configDirectory} with env set to ${env}, bank set to ${bank} and entity set to ${entity}.`) : console.log(`Loading config from ${configDirectory} with env set to ${env} and bank set to ${bank}.`); + + global.entity = entity; const baseConfigFile = path.join(configDirectory, 'config.json'); - const envConfigFile = env ? path.join(configDirectory, `config.${env}.json`) : null; + const envConfigFile = env ? entity ? path.join(configDirectory, `config.${env}.${bank}.${entity}.json`) : path.join(configDirectory, `config.${env}.${bank}.json`) : null; return { ...safeLoadJson(baseConfigFile), From 500737dbc75bbe717da57c97d9c1a8fc125fbc0f Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Mon, 29 Mar 2021 09:12:23 +0200 Subject: [PATCH 6/9] Remove not needed else statement Cleanup as per comment --- lib/crypto/Crypto.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/crypto/Crypto.js b/lib/crypto/Crypto.js index a2032b5..4d53560 100644 --- a/lib/crypto/Crypto.js +++ b/lib/crypto/Crypto.js @@ -78,13 +78,8 @@ module.exports = class Crypto { { console.log("hex string of key starts with \"00\" and is 514 bytes long, fixing it to be 512 bytes long by stripping leading \"00\""); hexString = hexString.substr(2); - var base64String = Buffer.from(hexString, 'hex').toString('base64') - return base64String; - } - else - { - return (modPow(base, power, mod)).toBEBuffer().toString('base64'); } + return Buffer.from(hexString, 'hex').toString('base64'); } static pad(d) { From 374560b14a0bc55b3bc7b73b886652aee6ed5de8 Mon Sep 17 00:00:00 2001 From: Herman van Hazendonk Date: Mon, 29 Mar 2021 14:53:27 +0200 Subject: [PATCH 7/9] README.md: Add J.P. Morgan to tested banks J.P. Morgan has been tested for their global EBICS channel as well and confirmed to be working. Signed-off-by: Herman van Hazendonk --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b14d96d..65932b4 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ The client is currently tested and verified to work with the following banks: * [BW Bank](https://www.bw-bank.de/de/home.html) * [Bank GPB International S.A.](https://gazprombank.lu/e-banking) * [Bank GPB AO](https://gazprombank.ru/) +* [J.P. Morgan](https://www.jpmorgan.com/) ## Inspiration From 6921f8d50364ddfa25289a0ee00e521eeb0bf491 Mon Sep 17 00:00:00 2001 From: nanov Date: Tue, 30 Mar 2021 09:20:21 +0300 Subject: [PATCH 8/9] chore: cleanup crypto sign fucntion --- lib/crypto/Crypto.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/crypto/Crypto.js b/lib/crypto/Crypto.js index 4d53560..02dca59 100644 --- a/lib/crypto/Crypto.js +++ b/lib/crypto/Crypto.js @@ -67,19 +67,16 @@ module.exports = class Crypto { } static sign(key, msg, salt = crypto.randomBytes(32)) { - // console.log(key.d()); const base = new BigNumber(emsaPSS(msg, salt)); const power = new BigNumber(key.d()); const mod = new BigNumber(key.n()); + const buffer = modPow(base, power, mod).toBEBuffer(); - //Somehow sometimes we have a 514 byte hex key that starts with "00". In that case use only the last 512 bytes - var hexString = modPow(base, power, mod).toBEBuffer().toString('hex'); - if(hexString.substr(0,2) === "00" && hexString.length == 514) - { - console.log("hex string of key starts with \"00\" and is 514 bytes long, fixing it to be 512 bytes long by stripping leading \"00\""); - hexString = hexString.substr(2); - } - return Buffer.from(hexString, 'hex').toString('base64'); + if (buffer.byteLength !== 257 || buffer[0] === 0x00) + return buffer.slice(1).toString('base64'); + + // console.log('hex string of key starts with "00" and is 514 bytes long, fixing it to be 512 bytes long by stripping leading "00"'); + return buffer.toString('base64'); } static pad(d) { From f6dfc73ff2575306fb121db0c3f369209bbadeed Mon Sep 17 00:00:00 2001 From: nanov Date: Tue, 30 Mar 2021 09:21:34 +0300 Subject: [PATCH 9/9] chore: add https://github.com/Herrie82 to contributors --- package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/package.json b/package.json index 4e757de..d528006 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,10 @@ { "name": "chrwoizi", "url": "https://github.com/chrwoizi" + }, + { + "name": "Herrie", + "url": "https://github.com/Herrie82" } ], "license": "GPL-3.0-only",