From e41dfce5280aee70fbac2ced3d38b2ebf3d7d1dd Mon Sep 17 00:00:00 2001 From: Nazar Kovtun Date: Fri, 16 May 2025 16:55:03 +0300 Subject: [PATCH 1/4] HCK-10586: add escaping for ipv6 host --- package-lock.json | 10 ++++- package.json | 3 +- .../helpers/connectionHelper.js | 3 +- .../helpers/escapeV6IPForURL.js | 39 +++++++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 reverse_engineering/helpers/escapeV6IPForURL.js diff --git a/package-lock.json b/package-lock.json index 5af397e..b186375 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,11 +6,13 @@ "packages": { "": { "name": "CosmosDB-with-Mongo-API", - "version": "0.2.3", + "version": "0.2.4", + "hasInstallScript": true, "dependencies": { "async": "3.2.6", "axios": "1.7.4", "bson": "1.1.5", + "ip": "2.0.1", "lodash": "4.17.21", "mongodb": "3.7.4", "qs": "6.12.1" @@ -2636,6 +2638,12 @@ "node": ">= 0.4" } }, + "node_modules/ip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", + "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", + "license": "MIT" + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", diff --git a/package.json b/package.json index 2d29c56..0914ab3 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "async": "3.2.6", "axios": "1.7.4", "bson": "1.1.5", + "ip": "2.0.1", "lodash": "4.17.21", "mongodb": "3.7.4", "qs": "6.12.1" @@ -94,4 +95,4 @@ "prettier": "3.2.5", "simple-git-hooks": "2.11.1" } -} \ No newline at end of file +} diff --git a/reverse_engineering/helpers/connectionHelper.js b/reverse_engineering/helpers/connectionHelper.js index fd0d3bc..d279991 100644 --- a/reverse_engineering/helpers/connectionHelper.js +++ b/reverse_engineering/helpers/connectionHelper.js @@ -1,8 +1,9 @@ const MongoClient = require('mongodb').MongoClient; +const { escapeV6IpForURL } = require('./escapeV6IPForURL'); function generateConnectionParams(connectionInfo) { return { - url: `mongodb://${connectionInfo.userName}:${connectionInfo.password}@${connectionInfo.host}:${connectionInfo.port}?ssl=true`, + url: `mongodb://${connectionInfo.userName}:${connectionInfo.password}@${escapeV6IpForURL({ host: connectionInfo.host })}:${connectionInfo.port}?ssl=true`, options: { sslValidate: false, useNewUrlParser: true, diff --git a/reverse_engineering/helpers/escapeV6IPForURL.js b/reverse_engineering/helpers/escapeV6IPForURL.js new file mode 100644 index 0000000..af16cbb --- /dev/null +++ b/reverse_engineering/helpers/escapeV6IPForURL.js @@ -0,0 +1,39 @@ +/* + * Copyright © 2016-2025 by IntegrIT S.A. dba Hackolade. All rights reserved. + * + * The copyright to the computer software herein is the property of IntegrIT S.A. + * The software may be used and/or copied only with the written permission of + * IntegrIT S.A. or in accordance with the terms and conditions stipulated in + * the agreement/contract under which the software has been supplied. + */ + +const ip = require('ip'); + +//@see https://en.wikipedia.org/wiki/IPv6_address +// Literal IPv6 addresses in resources (URLs): +// ------------------------------------------------ +// Colon (:) characters in IPv6 addresses may conflict with the established syntax of resource identifiers, +// such as URIs and URLs. The colon is conventionally used to terminate the host path before a port number.[10] +// To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers; +// When the URL doesn't conatoin the port the notation is +// http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/ +// When the URL also contains a port number the notation is: +// https://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443/ +function escapeV6IpForURL({ host }) { + // If the host is already URL compatible then the ip lib will return false + // > ip.isV6Format('[::1]') + // false + // If the host is a proper ipv6 ip then the `new URL(host)` will fail with Uncaught TypeError: Invalid URL + // code: 'ERR_INVALID_URL', + // !ip.isV4Format(host) check required because isV6Format returns true for ipv4 address because of backward compatibility + + if (ip.isV6Format(host) && !ip.isV4Format(host)) { + return `[${host}]`; + } + + return host; +} + +module.exports = { + escapeV6IpForURL, +}; From 539854fe20c2b5ef6da8d3e07d0c840930141bf6 Mon Sep 17 00:00:00 2001 From: Nazar Kovtun Date: Fri, 16 May 2025 16:57:24 +0300 Subject: [PATCH 2/4] HCK-10586: revert version bump --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index b186375..100c8ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "CosmosDB-with-Mongo-API", - "version": "0.2.4", + "version": "0.2.3", "hasInstallScript": true, "dependencies": { "async": "3.2.6", From 33a023cee3ee797f2a2f7888cf58f4f42c61011b Mon Sep 17 00:00:00 2001 From: Nazar Kovtun Date: Mon, 19 May 2025 13:05:55 +0300 Subject: [PATCH 3/4] HCK-10586: enchanced ipv6 escaping logic --- .../helpers/escapeV6IPForURL.js | 70 ++++++++++++++----- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/reverse_engineering/helpers/escapeV6IPForURL.js b/reverse_engineering/helpers/escapeV6IPForURL.js index af16cbb..c7f9bde 100644 --- a/reverse_engineering/helpers/escapeV6IPForURL.js +++ b/reverse_engineering/helpers/escapeV6IPForURL.js @@ -9,29 +9,63 @@ const ip = require('ip'); -//@see https://en.wikipedia.org/wiki/IPv6_address -// Literal IPv6 addresses in resources (URLs): -// ------------------------------------------------ -// Colon (:) characters in IPv6 addresses may conflict with the established syntax of resource identifiers, -// such as URIs and URLs. The colon is conventionally used to terminate the host path before a port number.[10] -// To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers; -// When the URL doesn't conatoin the port the notation is -// http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/ -// When the URL also contains a port number the notation is: -// https://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443/ +/** + * @see https://en.wikipedia.org/wiki/IPv6_address + * Literal IPv6 addresses in resources (URLs): +------------------------------------------------ + * Colon (:) characters in IPv6 addresses may conflict with the established syntax of resource identifiers, + * such as URIs and URLs. The colon is conventionally used to terminate the host path before a port number.[10] + * To alleviate this conflict, literal IPv6 addresses are enclosed in square brackets in such resource identifiers; + * When the URL doesn't conatoin the port the notation is http://[2001:db8:85a3:8d3:1319:8a2e:370:7348]/ + * When the URL also contains a port number the notation is: https://[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443/ + * + * @param {{ +* host: string +* }} param +* @returns {string} +*/ function escapeV6IpForURL({ host }) { - // If the host is already URL compatible then the ip lib will return false - // > ip.isV6Format('[::1]') - // false - // If the host is a proper ipv6 ip then the `new URL(host)` will fail with Uncaught TypeError: Invalid URL - // code: 'ERR_INVALID_URL', - // !ip.isV4Format(host) check required because isV6Format returns true for ipv4 address because of backward compatibility - + /** + * If the host is already URL compatible then the ip lib will return false > ip.isV6Format('[::1]') false + * If the host is a proper ipv6 ip then the `new URL(host)` will fail with Uncaught TypeError: Invalid URL code: 'ERR_INVALID_URL', + * !ip.isV4Format(host) check required because isV6Format returns true for ipv4 address because of backward compatibility + */ if (ip.isV6Format(host) && !ip.isV4Format(host)) { return `[${host}]`; } - return host; + const isUrlValid = isValidURL(host); + if (isUrlValid) { + return host; + } + + const urlWithIpV6HostRegExp = new RegExp(/^http(s)?:\/\/(?([a-z0-9]{0,4}:?)+)/gim); + const { unescapedIpWithPort } = urlWithIpV6HostRegExp.exec(host)?.groups ?? {}; + + if (!unescapedIpWithPort) { + return host; + } + + const separatedIpPortionsAndPort = unescapedIpWithPort.split(':'); + const ipPortions = separatedIpPortionsAndPort.slice(0, separatedIpPortionsAndPort.length - 1); + const port = separatedIpPortionsAndPort.at(-1); + const escapedIpWithPort = `[${ipPortions.join(':')}]:${port}`; + + return host.replace(unescapedIpWithPort, escapedIpWithPort); +} + +/** + * @param {string} url + * @returns {boolean} + */ +function isValidURL(url) { + try { + new URL(url); + + return true; + } catch { + return false; + } } module.exports = { From 4c9439c234473d31cbcc965aeb66a97ac1d6d48b Mon Sep 17 00:00:00 2001 From: Nazar Kovtun Date: Mon, 19 May 2025 13:07:58 +0300 Subject: [PATCH 4/4] HCK-10586: remove redundant licensing data --- reverse_engineering/helpers/escapeV6IPForURL.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/reverse_engineering/helpers/escapeV6IPForURL.js b/reverse_engineering/helpers/escapeV6IPForURL.js index c7f9bde..6ee75ef 100644 --- a/reverse_engineering/helpers/escapeV6IPForURL.js +++ b/reverse_engineering/helpers/escapeV6IPForURL.js @@ -1,12 +1,3 @@ -/* - * Copyright © 2016-2025 by IntegrIT S.A. dba Hackolade. All rights reserved. - * - * The copyright to the computer software herein is the property of IntegrIT S.A. - * The software may be used and/or copied only with the written permission of - * IntegrIT S.A. or in accordance with the terms and conditions stipulated in - * the agreement/contract under which the software has been supplied. - */ - const ip = require('ip'); /**