From 04d998def6a1dc6a50e5e4dd63537bbade59006a Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Mon, 6 Jul 2026 15:47:45 -0400 Subject: [PATCH 1/2] MLE-30256 Encode accessTokenDuration --- lib/marklogic.js | 6 ++++- lib/requester.js | 2 +- test-basic/cloud_authentication-test.js | 36 ++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/marklogic.js b/lib/marklogic.js index be579a34..8a93ad34 100644 --- a/lib/marklogic.js +++ b/lib/marklogic.js @@ -1,5 +1,5 @@ /* -* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ 'use strict'; const http = require('http'); @@ -761,6 +761,10 @@ function initClient(client, inputParams) { if(!connectionParams.apiKey) { throw new Error('apiKey needed for MarkLogic cloud authentication.'); } + if (connectionParams.accessTokenDuration !== undefined && + (!Number.isInteger(connectionParams.accessTokenDuration) || connectionParams.accessTokenDuration <= 0)) { + throw new Error('accessTokenDuration must be a positive integer.'); + } connectionParams.port = 443; } else if(authType === 'OAUTH' && !connectionParams.oauthToken){ throw new Error('oauthToken required for OAuth authentication. '); diff --git a/lib/requester.js b/lib/requester.js index 78b211f0..0dec0d99 100644 --- a/lib/requester.js +++ b/lib/requester.js @@ -46,7 +46,7 @@ function getAuthenticatorKerberos(client) { function getAccessToken(operation){ const postData = `${encodeURI('grant_type')}=${encodeURI('apikey')}&${encodeURI('key')}=${encodeURI(operation.client.connectionParams.apiKey)}`; - const path = operation.client.connectionParams.accessTokenDuration?'/token?duration='+operation.client.connectionParams.accessTokenDuration:'/token'; + const path = operation.client.connectionParams.accessTokenDuration?'/token?duration='+encodeURIComponent(operation.client.connectionParams.accessTokenDuration):'/token'; const options = { hostname: operation.client.connectionParams.host, port: 443, diff --git a/test-basic/cloud_authentication-test.js b/test-basic/cloud_authentication-test.js index 8eb8f75e..1979c225 100644 --- a/test-basic/cloud_authentication-test.js +++ b/test-basic/cloud_authentication-test.js @@ -1,5 +1,5 @@ /* -* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ const marklogic = require('../'); @@ -53,4 +53,38 @@ describe('cloud-authentication tests', function() { done(error); } }); + + it('should produce the correct plain token path when accessTokenDuration is a valid integer', function() { + const operation = { + client: { + connectionParams: { + host: 'example.marklogic.cloud', + apiKey: 'test-key', + accessTokenDuration: 300 + } + } + }; + + const duration = operation.client.connectionParams.accessTokenDuration; + const path = duration + ? '/token?duration=' + encodeURIComponent(duration) + : '/token'; + + assert.strictEqual(path, '/token?duration=300'); + }); + + it('should throw an error when accessTokenDuration is not a positive integer', function() { + try { + marklogic.createDatabaseClient({ + host: 'example.marklogic.cloud', + authType: 'cloud', + apiKey: 'test-key', + accessTokenDuration: '100&extra=injected' + }); + throw new Error('Expected validation error was not thrown'); + } catch (error) { + assert(error.message.includes('accessTokenDuration must be a positive integer'), + 'Error message should mention accessTokenDuration validation'); + } + }); }); From 6f29a5e1037472d2f334e67f7f363417c3fd4d83 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 7 Jul 2026 08:51:14 -0400 Subject: [PATCH 2/2] MLE-30256 Refactored test --- test-basic/cloud_authentication-test.js | 26 ++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/test-basic/cloud_authentication-test.js b/test-basic/cloud_authentication-test.js index 1979c225..08098460 100644 --- a/test-basic/cloud_authentication-test.js +++ b/test-basic/cloud_authentication-test.js @@ -54,23 +54,35 @@ describe('cloud-authentication tests', function() { } }); - it('should produce the correct plain token path when accessTokenDuration is a valid integer', function() { + it('should URL-encode accessTokenDuration when building the token request path', function() { + const requester = require('../lib/requester'); + const https = require('https'); + const operation = { client: { connectionParams: { host: 'example.marklogic.cloud', apiKey: 'test-key', - accessTokenDuration: 300 + accessTokenDuration: '100&extra=injected' } } }; - const duration = operation.client.connectionParams.accessTokenDuration; - const path = duration - ? '/token?duration=' + encodeURIComponent(duration) - : '/token'; + let capturedPath; + const originalRequest = https.request; + https.request = (options) => { + capturedPath = options.path; + throw new Error('stop'); + }; - assert.strictEqual(path, '/token?duration=300'); + try { + requester.getAccessToken(operation); + } catch (e) { + // ignore stubbed error + } finally { + https.request = originalRequest; + } + assert.strictEqual(capturedPath, '/token?duration=100%26extra%3Dinjected'); }); it('should throw an error when accessTokenDuration is not a positive integer', function() {