From b190d8a312ec3d1dbe7ee987b3cbb4c12956ff5a Mon Sep 17 00:00:00 2001 From: Markus Frindt Date: Wed, 21 Aug 2024 17:47:06 +0200 Subject: [PATCH] improvement of ipv4 regex and test cases (#73) * improvement of ipv4 regex and test cases * require utils test * change example --------- Co-authored-by: Markus Frindt --- lib/sip-trunk-register.js | 3 +- lib/utils.js | 16 +++++++- package-lock.json | 80 +++++++++++++++++++++------------------ test/index.js | 1 + test/utils.js | 42 ++++++++++++++++++++ 5 files changed, 104 insertions(+), 38 deletions(-) create mode 100644 test/utils.js diff --git a/lib/sip-trunk-register.js b/lib/sip-trunk-register.js index 6988e10..5102189 100644 --- a/lib/sip-trunk-register.js +++ b/lib/sip-trunk-register.js @@ -7,6 +7,7 @@ const { } = require('./config'); const assert = require('assert'); const short = require('short-uuid'); +const {isValidIPv4} = require('./utils'); const DEFAULT_EXPIRES = (parseInt(JAMBONES_REGBOT_DEFAULT_EXPIRES_INTERVAL) || 3600); const MIN_EXPIRES = (parseInt(JAMBONES_REGBOT_MIN_EXPIRES_INTERVAL) || 30); const MAX_INITIAL_DELAY = 15; @@ -124,7 +125,7 @@ class Regbot { } this.logger.debug(`sending REGISTER for ${this.aor}`); - const isIPv4 = /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.test(this.ipv4); + const isIPv4 = isValidIPv4(this.ipv4); const proxy = `sip:${this.ipv4}${isIPv4 ? `:${this.port}` : ''};transport=${transport}`; this.logger.debug({isIPv4}, `sending via proxy ${proxy}`); diff --git a/lib/utils.js b/lib/utils.js index 0b89832..f80b01a 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -27,9 +27,23 @@ async function addSipGatewayToBlacklist(client, logger, sip_gateway_sid, expired } } +/* Regex pattern to match valid IPv4 addresses (0.0.0.0 to 255.255.255.255) */ +const ipv4Pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; + +/** + * Checks if the given input string represents a valid IPv4 address. + * + * @param {string} ip - The string to be validated. + * @returns {boolean} - `true` if the input is a valid IPv4 address, `false` otherwise. + */ +function isValidIPv4(ip) { + return ipv4Pattern.test(ip); +} + module.exports = { isUacBehindNat, getSipProtocol, addSipGatewayToBlacklist, - NAT_EXPIRES: 30 + NAT_EXPIRES: 30, + isValidIPv4, }; diff --git a/package-lock.json b/package-lock.json index 3190d2d..66ed9a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -763,13 +763,13 @@ } }, "node_modules/@jambonz/db-helpers": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.4.tgz", - "integrity": "sha512-/YEj3lLH4ypWG9fe5xNOzipcgiwnBAk5Y0aoeW1c2THGyBqmo0FMkCoh3lebiU2OgkeIwrsoamNMa7ildQkXVg==", + "version": "0.9.7", + "resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.7.tgz", + "integrity": "sha512-5qN/CJZJXpbMkMn+8gFn8PpQ0ZImZxp1EjKyxLUlmMn+xgjeNb29c3pjeVt/6EQnBB65jAax6TNsVzVIfpvE2w==", "dependencies": { "cidr-matcher": "^2.1.1", "debug": "^4.3.4", - "mysql2": "^2.3.3", + "mysql2": "^3.11.0", "node-object-hash": "^2.3.10", "uuid": "^8.3.2" } @@ -1115,6 +1115,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.1.tgz", + "integrity": "sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3273,14 +3281,15 @@ "dev": true }, "node_modules/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -3359,16 +3368,17 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mysql2": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", - "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.11.0.tgz", + "integrity": "sha512-J9phbsXGvTOcRVPR95YedzVSxJecpW5A5+cQ57rhHIFXteTP10HCs+VBjS7DHIKfEaI1zQ5tlVrquCd64A6YvA==", "dependencies": { - "denque": "^2.0.1", + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.6.3", - "long": "^4.0.0", - "lru-cache": "^6.0.0", - "named-placeholders": "^1.1.2", + "long": "^5.2.1", + "lru-cache": "^8.0.0", + "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" }, @@ -3376,31 +3386,33 @@ "node": ">= 8.0" } }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" + } + }, "node_modules/named-placeholders": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", - "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", "dependencies": { - "lru-cache": "^4.1.3" + "lru-cache": "^7.14.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=12.0.0" } }, "node_modules/named-placeholders/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" } }, - "node_modules/named-placeholders/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, "node_modules/nan": { "version": "2.16.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.16.0.tgz", @@ -3939,11 +3951,6 @@ "node": ">=0.4.0" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, "node_modules/punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -4958,7 +4965,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/test/index.js b/test/index.js index 32b21e4..85361d4 100644 --- a/test/index.js +++ b/test/index.js @@ -4,3 +4,4 @@ require('./create-test-db'); require('./sip-register-tests'); require('./sip-options-tests'); require('./docker_stop'); +require('./utils'); diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..c3bc841 --- /dev/null +++ b/test/utils.js @@ -0,0 +1,42 @@ +const test = require('tape'); +const clearModule = require('clear-module'); +const { isValidIPv4 } = require('../lib/utils'); + +process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); +}); + +const testData = [ + { input: "192.168.1.1", expectedOutput: true, description: "Valid IPv4 address" }, + { input: "10.0.0.1", expectedOutput: true, description: "Valid IPv4 address" }, + { input: "255.255.255.255", expectedOutput: true, description: "Valid IPv4 address (broadcast)" }, + { input: "0.0.0.0", expectedOutput: true, description: "Valid IPv4 address (unspecified)" }, + { input: "127.0.0.1", expectedOutput: true, description: "Valid IPv4 address (localhost)" }, + { input: "1.2.3.4", expectedOutput: true, description: "Valid IPv4 address" }, + { input: "192.168.0.255", expectedOutput: true, description: "Valid IPv4 address" }, + { input: "255.0.0.0", expectedOutput: true, description: "Valid IPv4 address" }, + { input: "", expectedOutput: false, description: "Empty string" }, + { input: "192.168.1", expectedOutput: false, description: "Missing octet" }, + { input: "192.168.1.256", expectedOutput: false, description: "Octet out of range" }, + { input: "192.168.1.2.3", expectedOutput: false, description: "Too many octets" }, + { input: "192.168.a.1", expectedOutput: false, description: "Non-numeric character" }, + { input: "192..1.1", expectedOutput: false, description: "Consecutive dots" }, + { input: ".192.168.1.1", expectedOutput: false, description: "Leading dot" }, + { input: "192.168.1.1.", expectedOutput: false, description: "Trailing dot" }, + { input: " 192.168.1.1 ", expectedOutput: false, description: "Leading/trailing spaces" }, + { input: "192,168,1,1", expectedOutput: false, description: "Commas instead of dots" }, + { input: "2001:0db8:85a3:0000:0000:8a2e:0370:7334", expectedOutput: false, description: "IPv6 address" }, + { input: "localhost", expectedOutput: false, description: "Hostname, not IP" }, + { input: "10.0.0.1/24", expectedOutput: false, description: "IPv4 with subnet mask" }, + { input: "991240413047.primary.companyflex.de:0", expectedOutput: false, description: "Hostname with port, not IP" }, + ]; + +test('register tests', (t) => { + clearModule.all(); + + for (const data of testData) { + t.equal(isValidIPv4(data.input), data.expectedOutput, data.description); + } + + t.end(); +});