From eb82c2601d8572903f5c96bdecfeb0f33119c8f5 Mon Sep 17 00:00:00 2001 From: Glynn Bird Date: Fri, 6 Feb 2026 10:22:43 +0000 Subject: [PATCH 1/4] ensure that username and password returned from new URL are decoded before use --- lib/nano.js | 2 +- test/nano.basicauth.test.js | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/nano.basicauth.test.js diff --git a/lib/nano.js b/lib/nano.js index f21389a..ad5dfda 100644 --- a/lib/nano.js +++ b/lib/nano.js @@ -56,7 +56,7 @@ module.exports = exports = function dbScope (cfg) { cfg.plainURL = `${cfg.parsedURL.origin}${cfg.parsedURL.pathname}` cfg.headers = cfg.headers || {} if (cfg.parsedURL.username && cfg.parsedURL.password) { - cfg.headers.Authorization = 'Basic ' + Buffer.from(`${cfg.parsedURL.username}:${cfg.parsedURL.password}`).toString('base64') + cfg.headers.Authorization = 'Basic ' + Buffer.from(`${decodeURIComponent(cfg.parsedURL.username)}:${decodeURIComponent(cfg.parsedURL.password)}`).toString('base64') } // look for agentOptions diff --git a/test/nano.basicauth.test.js b/test/nano.basicauth.test.js new file mode 100644 index 0000000..30a3b9d --- /dev/null +++ b/test/nano.basicauth.test.js @@ -0,0 +1,36 @@ +// Licensed under the Apache License, Version 2.0 (the 'License'); you may not +// use this file except in compliance with the License. You may obtain a copy of +// the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +const test = require('node:test') +const assert = require('node:assert/strict') +const COUCH_URL="http://admin[:admin]@localhost:5984" +const { mockAgent, mockPool, JSON_HEADERS } = require('./mock.js') +const Nano = require('..') +const nano = Nano(COUCH_URL) + +test('should handle special characters in username & password', async () => { + // mocks + const auth = 'Basic ' + Buffer.from('admin[:admin]').toString('base64') + mockPool + .intercept({ + path: '/_all_dbs', + headers: { + 'Authorization': auth + } + }) + .reply(200, ['a'], JSON_HEADERS) + + // test POST /_session + const q = await nano.db.list() + assert.deepEqual(q, ['a']) + mockAgent.assertNoPendingInterceptors() +}) From f30b5349a24996586aa9bb37a7c55b71fdba885c Mon Sep 17 00:00:00 2001 From: Glynn Bird Date: Fri, 6 Feb 2026 10:41:40 +0000 Subject: [PATCH 2/4] tests working --- test/nano.basicauth.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/nano.basicauth.test.js b/test/nano.basicauth.test.js index 30a3b9d..1031f35 100644 --- a/test/nano.basicauth.test.js +++ b/test/nano.basicauth.test.js @@ -12,10 +12,11 @@ const test = require('node:test') const assert = require('node:assert/strict') -const COUCH_URL="http://admin[:admin]@localhost:5984" +const COUCH_URL="http://admin[:admin]@127.0.0.1:5984" const { mockAgent, mockPool, JSON_HEADERS } = require('./mock.js') const Nano = require('..') -const nano = Nano(COUCH_URL) +const nano = Nano({ url: COUCH_URL }) +const pkg = require('../package.json') test('should handle special characters in username & password', async () => { // mocks @@ -24,7 +25,8 @@ test('should handle special characters in username & password', async () => { .intercept({ path: '/_all_dbs', headers: { - 'Authorization': auth + 'authorization': auth, + 'content-type': 'application/json' } }) .reply(200, ['a'], JSON_HEADERS) From 3edd00b5a5c76412e3e585b3d7a4844ec63036e4 Mon Sep 17 00:00:00 2001 From: Glynn Bird Date: Fri, 6 Feb 2026 10:53:48 +0000 Subject: [PATCH 3/4] added node 24 to the testing matrix --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6584615..221d04f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,7 @@ jobs: strategy: matrix: - node-version: [20.x, 22.x] + node-version: [20.x, 22.x, 24.x] steps: - uses: actions/checkout@v3 From 489aea1863fca41001689b19639619071acff82a Mon Sep 17 00:00:00 2001 From: Glynn Bird Date: Fri, 6 Feb 2026 10:54:24 +0000 Subject: [PATCH 4/4] 11.0.4 + dependency bump --- package-lock.json | 36 ++++++++++++++++++------------------ package.json | 8 ++++---- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7987105..d5fbaac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,36 +1,36 @@ { "name": "nano", - "version": "11.0.3", + "version": "11.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "nano", - "version": "11.0.3", + "version": "11.0.4", "license": "Apache-2.0", "devDependencies": { - "@types/node": "^24.4.0", - "typescript": "^5.9.2", - "undici": "^7.16.0" + "@types/node": "^25.2.1", + "typescript": "^5.9.3", + "undici": "^7.20.0" }, "engines": { "node": ">=20.0" } }, "node_modules/@types/node": { - "version": "24.4.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.4.0.tgz", - "integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==", + "version": "25.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz", + "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.11.0" + "undici-types": "~7.16.0" } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -42,9 +42,9 @@ } }, "node_modules/undici": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", - "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.20.0.tgz", + "integrity": "sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ==", "dev": true, "license": "MIT", "engines": { @@ -52,9 +52,9 @@ } }, "node_modules/undici-types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.11.0.tgz", - "integrity": "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" } diff --git a/package.json b/package.json index 483d1f8..6048c3e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "license": "Apache-2.0", "homepage": "http://github.com/apache/couchdb-nano", "repository": "http://github.com/apache/couchdb-nano", - "version": "11.0.3", + "version": "11.0.4", "author": "Apache CouchDB (http://couchdb.apache.org)", "keywords": [ "couchdb", @@ -19,9 +19,9 @@ "dependencies": { }, "devDependencies": { - "undici": "^7.16.0", - "@types/node": "^24.4.0", - "typescript": "^5.9.2" + "undici": "^7.20.0", + "@types/node": "^25.2.1", + "typescript": "^5.9.3" }, "scripts": { "test": "tsc lib/nano.d.ts && node --test ./test/*.test.js"