From 80dbcaa12e42943bc7076b7a2e28df1b99a4571b Mon Sep 17 00:00:00 2001 From: Kris Maschi Date: Mon, 28 Apr 2025 10:13:07 -0400 Subject: [PATCH 1/2] Release/5.3.1 (#248) * feat(metadata): initialize dotenv and include API mesh environment variables (#247) * feat(metadata): initialize dotenv and include API mesh environment variables * fix(helpers): reformat API mesh environment variable headers * fix(helpers): improve header formatting for API mesh environment variables * fix(helpers): improve header formatting for API mesh environment variables * feat(constants): add dotenv support and export environment variables for API mesh --------- Co-authored-by: Kristopher Maschi * Update package.json to 5.3.1 --------- Co-authored-by: Kristopher Maschi --- package.json | 2 +- src/constants.js | 16 +++++++++++++++- src/helpers.js | 4 +--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 691bc6cd..67ab51f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/aio-cli-plugin-api-mesh", - "version": "5.3.0", + "version": "5.3.1", "description": "Adobe I/O CLI plugin to develop and manage API mesh sources", "keywords": [ "oclif-plugin" diff --git a/src/constants.js b/src/constants.js index adee4e23..09bf8dd6 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,5 +1,7 @@ const { getCliEnv } = require('@adobe/aio-lib-env'); +const dotenv = require('dotenv'); +dotenv.config(); const clientEnv = getCliEnv(); const StageConstants = { @@ -24,4 +26,16 @@ const ProdConstants = { }; const envConstants = clientEnv === 'stage' ? StageConstants : ProdConstants; -module.exports = { ...envConstants }; + +// Export environment variables/constants +module.exports = { + DEV_CONSOLE_BASE_URL: process.env.DEV_CONSOLE_BASE_URL || envConstants.DEV_CONSOLE_BASE_URL, + DEV_CONSOLE_API_KEY: process.env.DEV_CONSOLE_API_KEY || envConstants.DEV_CONSOLE_API_KEY, + DEV_CONSOLE_TRANSPORTER_API_KEY: + process.env.DEV_CONSOLE_TRANSPORTER_API_KEY || envConstants.DEV_CONSOLE_TRANSPORTER_API_KEY, + AIO_CLI_API_KEY: process.env.AIO_CLI_API_KEY || envConstants.AIO_CLI_API_KEY, + SMS_BASE_URL: process.env.SMS_BASE_URL || envConstants.SMS_BASE_URL, + MESH_BASE_URL: process.env.MESH_BASE_URL || envConstants.MESH_BASE_URL, + MESH_SANDBOX_BASE_URL: process.env.MESH_SANDBOX_BASE_URL || envConstants.MESH_SANDBOX_BASE_URL, + SMS_API_KEY: process.env.SMS_API_KEY || envConstants.SMS_API_KEY, +}; diff --git a/src/helpers.js b/src/helpers.js index 04be1d33..9979ff5c 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -452,15 +452,13 @@ function initMetadata(config) { const { version, plugins, userAgent, platform, arch } = config; const currentIntalledVersion = getCurrentInstalledPluginVersion(plugins); - const metadataHeaders = { + global.metadataHeaders = { 'x-aio-cli-version': version, 'x-aio-cli-user-agent': userAgent, 'x-aio-cli-platform': platform, 'x-aio-cli-arch': arch, 'x-aio-cli-plugin-api-mesh-version': currentIntalledVersion, }; - - global.metadataHeaders = metadataHeaders; } catch (error) { logger.error('Unable to initialize metadata headers'); logger.error(error.message); From c5b7272ef5e1b00d69c9c0328c41d82bd92b6ab3 Mon Sep 17 00:00:00 2001 From: Sumaiya <108254100+AjazSumaiya@users.noreply.github.com> Date: Mon, 12 May 2025 21:01:48 +0530 Subject: [PATCH 2/2] Release/5.3.2 (#251) * feat: EOD commit, encyption of license key - WIP, need to fix tests * feat: fixing failing test * feat: bump alpha version * feat: modiefied code to address review comment and address for splunk key as well * feat: very minor correction * feat(metadata): initialize dotenv and include API mesh environment variables (#247) * feat(metadata): initialize dotenv and include API mesh environment variables * fix(helpers): reformat API mesh environment variable headers * fix(helpers): improve header formatting for API mesh environment variables * fix(helpers): improve header formatting for API mesh environment variables * feat(constants): add dotenv support and export environment variables for API mesh --------- Co-authored-by: Kristopher Maschi * Release/5.3.1 (#248) (#249) * feat(metadata): initialize dotenv and include API mesh environment variables (#247) * feat(metadata): initialize dotenv and include API mesh environment variables * fix(helpers): reformat API mesh environment variable headers * fix(helpers): improve header formatting for API mesh environment variables * fix(helpers): improve header formatting for API mesh environment variables * feat(constants): add dotenv support and export environment variables for API mesh --------- * Update package.json to 5.3.1 --------- Co-authored-by: Brasewel Noronha <100383619+brasewel@users.noreply.github.com> Co-authored-by: Kristopher Maschi * refactor: removed Newrelic license 40 char restriction * bump up package version * fix: updated keys to enum values * Update package version * Update package version * Apply suggestions from code review Co-authored-by: Brasewel Noronha <100383619+brasewel@users.noreply.github.com> * fix: linting * updated package version --------- Co-authored-by: Narendra Vyas Co-authored-by: Kris Maschi Co-authored-by: Kristopher Maschi Co-authored-by: Brasewel Noronha <100383619+brasewel@users.noreply.github.com> --- package.json | 2 +- .../__tests__/set-log-forwarding.test.js | 56 +++++++++++++++---- .../api-mesh/config/set/log-forwarding.js | 32 ++++++++++- src/utils.js | 13 +++-- yarn.lock | 2 +- 5 files changed, 84 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 67ab51f6..d268e78d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/aio-cli-plugin-api-mesh", - "version": "5.3.1", + "version": "5.3.2", "description": "Adobe I/O CLI plugin to develop and manage API mesh sources", "keywords": [ "oclif-plugin" diff --git a/src/commands/api-mesh/__tests__/set-log-forwarding.test.js b/src/commands/api-mesh/__tests__/set-log-forwarding.test.js index 52973194..fc322c2e 100644 --- a/src/commands/api-mesh/__tests__/set-log-forwarding.test.js +++ b/src/commands/api-mesh/__tests__/set-log-forwarding.test.js @@ -11,6 +11,7 @@ governing permissions and limitations under the License. */ const SetLogForwardingCommand = require('../config/set/log-forwarding'); +const crypto = require('crypto'); const { initSdk, promptConfirm, @@ -18,7 +19,7 @@ const { promptInput, promptInputSecret, } = require('../../../helpers'); -const { getMeshId, setLogForwarding } = require('../../../lib/smsClient'); +const { getMeshId, setLogForwarding, getPublicEncryptionKey } = require('../../../lib/smsClient'); jest.mock('../../../helpers', () => ({ initSdk: jest.fn().mockResolvedValue({}), @@ -31,6 +32,21 @@ jest.mock('../../../helpers', () => ({ jest.mock('../../../lib/smsClient'); jest.mock('../../../classes/logger'); +jest.mock('crypto'); +// Mock randomBytes for aesKey and iv +const mockAesKey = Buffer.from('mockAesKey'); +const mockIv = Buffer.from('mockIv'); +const mockEncryptedAesKey = Buffer.from('mockEncryptedAesKey'); +const mockCipher = { + update: jest.fn().mockReturnValueOnce('mockEncryptedData'), + final: jest.fn().mockReturnValueOnce(''), +}; +const mockEncryptedLicenseKey = { + iv: 'bW9ja0l2', + key: 'bW9ja0VuY3J5cHRlZEFlc0tleQ==', + data: 'mockEncryptedData', +}; + describe('SetLogForwardingCommand', () => { let parseSpy; let logSpy; @@ -60,8 +76,13 @@ describe('SetLogForwardingCommand', () => { workspaceName: 'workspaceName', }); getMeshId.mockResolvedValue('meshId'); + getPublicEncryptionKey.mockResolvedValue('dummy_public_key'); setLogForwarding.mockResolvedValue({ success: true, result: true }); global.requestId = 'dummy_request_id'; + + // Reset mockCipher methods + mockCipher.update.mockReset().mockReturnValueOnce('mockEncryptedData'); + mockCipher.final.mockReset().mockReturnValueOnce(''); }); afterEach(() => { @@ -71,6 +92,10 @@ describe('SetLogForwardingCommand', () => { describe('Test New Relic destination', () => { /** Success Scenario */ test('sets log forwarding with valid parameters', async () => { + crypto.randomBytes.mockReturnValueOnce(mockAesKey).mockReturnValueOnce(mockIv); + crypto.createCipheriv.mockReturnValueOnce(mockCipher); + crypto.publicEncrypt.mockReturnValueOnce(mockEncryptedAesKey); + const command = new SetLogForwardingCommand([], {}); await command.run(); @@ -88,7 +113,7 @@ describe('SetLogForwardingCommand', () => { destination: 'newrelic', config: { baseUri: 'https://log-api.newrelic.com/log/v1', - licenseKey: 'abcdef0123456789abcdef0123456789abcdef01', + licenseKey: JSON.stringify(mockEncryptedLicenseKey), // Expect the encrypted value }, }, ); @@ -115,15 +140,6 @@ describe('SetLogForwardingCommand', () => { ); }); - test('throws an error if license key has wrong format', async () => { - promptInputSecret.mockResolvedValueOnce('wrongformat'); // Too short - - const command = new SetLogForwardingCommand([], {}); - await expect(command.run()).rejects.toThrow( - `The license key is in the wrong format. Expected: 40 characters (received: ${11})`, - ); - }); - test('prompts for missing destination', async () => { parseSpy.mockResolvedValueOnce({ flags: { @@ -135,6 +151,10 @@ describe('SetLogForwardingCommand', () => { args: [], }); + crypto.randomBytes.mockReturnValueOnce(mockAesKey).mockReturnValueOnce(mockIv); + crypto.createCipheriv.mockReturnValueOnce(mockCipher); + crypto.publicEncrypt.mockReturnValueOnce(mockEncryptedAesKey); + const command = new SetLogForwardingCommand([], {}); await command.run(); @@ -195,6 +215,10 @@ describe('SetLogForwardingCommand', () => { args: [], }); + crypto.randomBytes.mockReturnValueOnce(mockAesKey).mockReturnValueOnce(mockIv); + crypto.createCipheriv.mockReturnValueOnce(mockCipher); + crypto.publicEncrypt.mockReturnValueOnce(mockEncryptedAesKey); + const command = new SetLogForwardingCommand([], {}); await command.run(); @@ -203,6 +227,10 @@ describe('SetLogForwardingCommand', () => { }); test('sets log forwarding with auto-confirmation', async () => { + crypto.randomBytes.mockReturnValueOnce(mockAesKey).mockReturnValueOnce(mockIv); + crypto.createCipheriv.mockReturnValueOnce(mockCipher); + crypto.publicEncrypt.mockReturnValueOnce(mockEncryptedAesKey); + parseSpy.mockResolvedValueOnce({ flags: { ignoreCache: false, @@ -225,7 +253,7 @@ describe('SetLogForwardingCommand', () => { destination: 'newrelic', config: { baseUri: 'https://log-api.newrelic.com/log/v1', - licenseKey: 'abcdef0123456789abcdef0123456789abcdef01', + licenseKey: JSON.stringify(mockEncryptedLicenseKey), // Expect the encrypted value }, }, ); @@ -236,6 +264,10 @@ describe('SetLogForwardingCommand', () => { const errorMessage = 'Unable to set log forwarding details'; setLogForwarding.mockRejectedValueOnce(new Error(errorMessage)); + crypto.randomBytes.mockReturnValueOnce(mockAesKey).mockReturnValueOnce(mockIv); + crypto.createCipheriv.mockReturnValueOnce(mockCipher); + crypto.publicEncrypt.mockReturnValueOnce(mockEncryptedAesKey); + const command = new SetLogForwardingCommand([], {}); await expect(command.run()).rejects.toThrow( 'Failed to set log forwarding details. Try again. RequestId: dummy_request_id', diff --git a/src/commands/api-mesh/config/set/log-forwarding.js b/src/commands/api-mesh/config/set/log-forwarding.js index 6c0db009..d6d3b91f 100644 --- a/src/commands/api-mesh/config/set/log-forwarding.js +++ b/src/commands/api-mesh/config/set/log-forwarding.js @@ -24,8 +24,14 @@ const { autoConfirmActionFlag, jsonFlag, destinations, + LogForwardingKeys, + encryptSecrets, } = require('../../../../utils'); -const { setLogForwarding, getMeshId } = require('../../../../lib/smsClient'); +const { + setLogForwarding, + getMeshId, + getPublicEncryptionKey, +} = require('../../../../lib/smsClient'); class SetLogForwardingCommand extends Command { static flags = { @@ -85,6 +91,30 @@ class SetLogForwardingCommand extends Command { if (shouldContinue) { try { + // Get publicKey for encryption + const publicKey = await getPublicEncryptionKey(imsOrgCode); + if (!publicKey) { + this.error( + `Unable to set log forwarding details. Unable to get public key. Try again. RequestId: ${global.requestId}`, + ); + } + // Get the key to encrypt from config + const getEncryptableKey = config => { + if (LogForwardingKeys.LICENSE_KEY in config) return LogForwardingKeys.LICENSE_KEY; + if (LogForwardingKeys.HEC_TOKEN in config) return LogForwardingKeys.HEC_TOKEN; + return null; + }; + const keyToEncrypt = getEncryptableKey(destinationConfig.config); + if (!keyToEncrypt) { + this.error( + `Unable to set log forwarding details. No valid key to encrypt found in the configuration. Try again. RequestId: ${global.requestId}`, + ); + } + // Encrypt the key + destinationConfig.config[keyToEncrypt] = await encryptSecrets( + publicKey, + destinationConfig.config[keyToEncrypt], + ); const response = await setLogForwarding( imsOrgCode, projectId, diff --git a/src/utils.js b/src/utils.js index 0946777f..a4f17b6c 100644 --- a/src/utils.js +++ b/src/utils.js @@ -101,6 +101,11 @@ const logFilenameFlag = Flags.string({ required: true, }); +const LogForwardingKeys = { + LICENSE_KEY: 'licenseKey', + HEC_TOKEN: 'hecToken', +}; + // The `destinations` object to hold the configuration for log forwarding destinations. // It prompts for the required inputs for the destination. // Each destination can have different key/value pairs of configuration credentials. @@ -132,11 +137,6 @@ const destinations = { if (!value) { throw new Error('License key is required'); } - if (value.length !== 40) { - throw new Error( - `The license key is in the wrong format. Expected: 40 characters (received: ${value.length})`, - ); - } }, }, ], @@ -777,7 +777,7 @@ async function localToUTCTime(timeString) { try { //Get the local timezone // takes the timezone where the javascript runtime is running - // reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#browser_compatibility:~:text=The%20value%20provided%20for%20this%20property%20in%20the%20options%20argument%2C%20with%20default%20filled%20in%20as%20needed.%20It%20is%20an%20IANA%20time%20zone%20name.%20The%20default%20is%20the%20runtime%27s%20default%20time%20zone. + // reference https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/resolvedOptions#browser_compatibility:~:text=The%20value%20provided%20for%20this%20property%20in%20the%20options%20argument%2C%20with%20default%20filled%20in%20as%20needed.%20It%20is%20an%20IANA%20time%20zone.%20The%20default%20is%20the%20runtime%27s%20default%20time%20zone. const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; // Create a Date object from the formatted time string @@ -824,4 +824,5 @@ module.exports = { localToUTCTime, cachePurgeAllActionFlag, destinations, + LogForwardingKeys, }; diff --git a/yarn.lock b/yarn.lock index 10b8c1b6..bc7133be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8208,7 +8208,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1, ms@^2.1.3: +ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==