From ad17e3afb3db3857e724e84087254acb0c9240f3 Mon Sep 17 00:00:00 2001 From: Devin Holderness Date: Tue, 22 Jul 2025 12:21:42 -0600 Subject: [PATCH 1/3] feat: add connection profile alias functionality --- src/ops/ConnectionProfileOps.test.ts | 302 ++++++++++++++++++++++++++- src/ops/ConnectionProfileOps.ts | 186 +++++++++++++++-- src/shared/State.ts | 14 +- 3 files changed, 474 insertions(+), 28 deletions(-) diff --git a/src/ops/ConnectionProfileOps.test.ts b/src/ops/ConnectionProfileOps.test.ts index 918c46cf7..4d540df09 100644 --- a/src/ops/ConnectionProfileOps.test.ts +++ b/src/ops/ConnectionProfileOps.test.ts @@ -8,9 +8,21 @@ */ import fs from 'fs'; import { homedir } from 'os'; -import { state } from '../index'; +import { FrodoError, state } from '../index'; import * as ConnectionProfileOps from './ConnectionProfileOps'; import Constants from '../shared/Constants'; +import { CONNECTOR_TYPE } from './ConnectorOps'; + +const exampleHost = 'https://openam-tenant-name.forgeblocks.com/am'; +const exampleUsername = 'frodo.baggins@shire.me'; +const examplePassword = 'G@nd@lfTheW153'; +const exampleConnectionProfile = { + tenant: exampleHost, + username: exampleUsername, + encodedPassword: examplePassword, + allowInsecureConnection: false, + deploymentType: 'classic', +}; describe('ConnectionProfileOps', () => { const connectionProfilePath1 = `${homedir()}/connections1.json`; @@ -36,6 +48,12 @@ describe('ConnectionProfileOps', () => { } }); + beforeEach(() => { + fs.writeFileSync(connectionProfilePath1, JSON.stringify({})); + fs.writeFileSync(connectionProfilePath2, JSON.stringify({})); + fs.writeFileSync(connectionProfilePath3, JSON.stringify({})); + }); + // clean up all connection profile files after running the tests afterAll(() => { try { @@ -55,11 +73,81 @@ describe('ConnectionProfileOps', () => { } }); + describe('findConnectionProfiles()', () => { + test.only('1: Find connection profile by alias', async () => { + const tenant = exampleHost + const alias = 'unique-alias'; + const host = alias; + const connectionProfiles = { + [tenant]: { + ...exampleConnectionProfile, + alias, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfiles, null, 2) + ); + + const connections = ConnectionProfileOps.findConnectionProfiles({ + connectionProfiles, + host, + state, + }); + expect(connections).toHaveLength(1); + expect(connections[0].tenant).toBe(tenant); + expect(connections[0].alias).toBe(alias); + }); + + test('2: Default to substring matching after failing to find connection profile by alias', async () => { + const tenant = exampleHost; + const host = 'name'; + const connectionProfiles = { + [tenant]: { + ...exampleConnectionProfile, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfiles, null, 2) + ); + + const connections = ConnectionProfileOps.findConnectionProfiles({ + connectionProfiles, + host, + state, + }); + expect(connections).toHaveLength(1); + expect(connections[0].tenant).toBe(tenant); + }); + + test('3: Fail to find a match by alias or substring', async () => { + const host = 'nonexistent' + const tenant = exampleHost; + const connectionProfiles = { + [tenant]: { + ...exampleConnectionProfile, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfiles, null, 2) + ); + + const connections = ConnectionProfileOps.findConnectionProfiles({ + connectionProfiles, + host, + state, + }); + expect(connections).toHaveLength(0); + }); + }); + describe('saveConnectionProfile()', () => { test('1: Create connection profiles in location from state field', async () => { - const host = 'https://openam-tenant-name.forgeblocks.com/am'; - const user = 'frodo.baggins@shire.me'; - const password = 'G@nd@lfTheW153'; + const host = exampleHost; + const user = exampleUsername; + const password = examplePassword; state.setHost(host); state.setDeploymentType(Constants.FORGEOPS_DEPLOYMENT_TYPE_KEY); @@ -81,9 +169,9 @@ describe('ConnectionProfileOps', () => { }); test(`2: Create connection profiles in location from env ${Constants.FRODO_MASTER_KEY_PATH_KEY}`, async () => { - const host = 'https://openam-tenant-name.forgeblocks.com/am'; - const user = 'frodo.baggins@shire.me'; - const password = 'G@nd@lfTheW153'; + const host = exampleHost; + const user = exampleUsername; + const password = examplePassword; // set the hard-coded master key process.env[Constants.FRODO_CONNECTION_PROFILES_PATH_KEY] = connectionProfilePath2; @@ -110,9 +198,9 @@ describe('ConnectionProfileOps', () => { }); test(`3: Use Master Key from env ${Constants.FRODO_MASTER_KEY_KEY}`, async () => { - const host = 'https://openam-tenant-name.forgeblocks.com/am'; - const user = 'frodo.baggins@shire.me'; - const password = 'G@nd@lfTheW153'; + const host = exampleHost; + const user = exampleUsername; + const password = examplePassword; const masterKey = 'bxnQlhcU5VfyDs+BBPhRhK09yHaNtdIIk85HUMKBnqg='; // set the hard-coded master key process.env[Constants.FRODO_MASTER_KEY_KEY] = masterKey; @@ -135,5 +223,199 @@ describe('ConnectionProfileOps', () => { expect(connections[host].username).toEqual(user); expect(connections[host].encodedPassword).toBeTruthy(); }); + + test(`4: Save a new connection with a unique alias`, async () => { + const host = exampleHost; + const alias = 'unique-alias'; + const user = exampleUsername; + const password = examplePassword; + + state.setConnectionProfilesPath(connectionProfilePath1); + state.setHost(host); + state.setAlias(alias); + state.setUsername(user); + state.setPassword(password); + state.setDeploymentType(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY); + await ConnectionProfileOps.saveConnectionProfile({ host, state }); + const connections = JSON.parse( + fs.readFileSync(connectionProfilePath1, 'utf8') + ); + expect(connections[host]).toBeTruthy(); + expect(connections[host].alias).toEqual(alias); + expect(connections[host].username).toEqual(user); + expect(connections[host].encodedPassword).toBeTruthy(); + }); + + test(`5: Fail to save a new connection with a conflicting alias`, async () => { + const host1 = 'https://openam-tenant1.forgeblocks.com/am'; + const host2 = 'https://openam-tenant2.forgeblocks.com/am'; + const alias = 'conflicting-alias'; + const user = exampleUsername; + const password = examplePassword; + + state.setConnectionProfilesPath(connectionProfilePath1); + state.setHost(host1); + state.setAlias(alias); + state.setUsername(user); + state.setPassword(password); + state.setDeploymentType(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY); + await ConnectionProfileOps.saveConnectionProfile({ host: host1, state }); + + state.setHost(host2); + state.setAlias(alias); + state.setUsername(user); + state.setPassword(password); + state.setDeploymentType(Constants.CLASSIC_DEPLOYMENT_TYPE_KEY); + + try { + await ConnectionProfileOps.saveConnectionProfile({ + host: host2, + state, + }); + } catch (e) { + expect(e).toBeInstanceOf(FrodoError); + const error = e as FrodoError; + expect(error.message).toBe('Error saving connection profile'); + expect(error.originalErrors[0]?.message).toBe( + `Alias '${alias}' is already in use by connection profile '${host1}'. Please use a unique alias.` + ); + } + expect.assertions(3); + }); + }); + + describe('setConnectionProfileAlias()', () => { + test(`1: Set unique alias for existing connection profile`, async () => { + const host = exampleHost; + const alias = 'unique-alias'; + const connectionProfile = { + [host]: { + ...exampleConnectionProfile, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfile, null, 2) + ); + + state.setConnectionProfilesPath(connectionProfilePath1); + state.setHost(host); + state.setAlias(alias); + await ConnectionProfileOps.setConnectionProfileAlias({ + host, + alias, + state, + }); + const connections = JSON.parse( + fs.readFileSync(connectionProfilePath1, 'utf8') + ); + expect(connections[host]).toBeTruthy(); + expect(connections[host].alias).toEqual(alias); + }); + + test(`2: Fail to set a conflicting alias to an existing connection profile`, async () => { + const host1 = 'https://openam-tenant1.forgeblocks.com/am'; + const host2 = 'https://openam-tenant2.forgeblocks.com/am'; + const alias = 'conflicting-alias'; + + const connectionProfiles = { + [host1]: { + ...exampleConnectionProfile, + tenant: host1, + }, + [host2]: { + ...exampleConnectionProfile, + tenant: host2, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfiles, null, 2) + ); + + state.setConnectionProfilesPath(connectionProfilePath1); + state.setHost(host1); + state.setAlias(alias); + await ConnectionProfileOps.setConnectionProfileAlias({ + host: host1, + alias, + state, + }); + + state.setHost(host2); + try { + await ConnectionProfileOps.setConnectionProfileAlias({ + host: host2, + alias, + state, + }); + } catch (e) { + expect(e).toBeInstanceOf(FrodoError); + const error = e as FrodoError; + expect(error.message).toBe( + `Alias '${alias}' is already in use by connection profile '${host1}'. Please use a unique alias.` + ); + } + expect.assertions(2); + }); + }); + + describe('deleteConnectionProfileAlias()', () => { + test(`1: Delete the alias of an existing connection profile`, async () => { + const host = exampleHost; + const alias = 'unique-alias'; + + const connectionProfile = { + [host]: { + ...exampleConnectionProfile, + alias: alias, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfile, null, 2) + ); + + state.setConnectionProfilesPath(connectionProfilePath1); + state.setHost(host); + await ConnectionProfileOps.deleteConnectionProfileAlias({ + host, + state, + }); + const connections = JSON.parse( + fs.readFileSync(connectionProfilePath1, 'utf8') + ); + expect(connections[host]).toBeTruthy(); + expect(connections[host].alias).toBeUndefined(); + }); + + test(`2: Fail to delete the nonexistent alias of an exisiting connection profile`, async () => { + const host = exampleHost; + const connectionProfile = { + [host]: { + ...exampleConnectionProfile, + }, + }; + fs.writeFileSync( + connectionProfilePath1, + JSON.stringify(connectionProfile, null, 2) + ); + + state.setConnectionProfilesPath(connectionProfilePath1); + state.setHost(host); + try { + await ConnectionProfileOps.deleteConnectionProfileAlias({ + host, + state, + }); + } catch (e) { + expect(e).toBeInstanceOf(FrodoError); + const error = e as FrodoError; + expect(error.message).toBe( + `No alias is set for connection profile '${host}'` + ); + } + expect.assertions(2); + }); }); }); diff --git a/src/ops/ConnectionProfileOps.ts b/src/ops/ConnectionProfileOps.ts index 7196a2149..8cf20df57 100644 --- a/src/ops/ConnectionProfileOps.ts +++ b/src/ops/ConnectionProfileOps.ts @@ -26,7 +26,7 @@ export type ConnectionProfile = { /** * Find connection profiles * @param {ConnectionsFileInterface} connectionProfiles connection profile object - * @param {string} host host url or unique substring + * @param {string} host host url, unique substring, or alias * @returns {SecureConnectionProfileInterface[]} Array of connection profiles */ findConnectionProfiles( @@ -42,7 +42,7 @@ export type ConnectionProfile = { initConnectionProfiles(): Promise; /** * Get connection profile by host - * @param {String} host host tenant host url or unique substring + * @param {String} host host tenant, host url, unique substring, or alias * @returns {Object} connection profile or null */ getConnectionProfileByHost(host: string): Promise; @@ -53,7 +53,7 @@ export type ConnectionProfile = { getConnectionProfile(): Promise; /** * Load a connection profile into library state - * @param {string} host AM host URL or unique substring + * @param {string} host AM host URL, unique substring, or alias * @returns {Promise} A promise resolving to true if successful */ loadConnectionProfileByHost(host: string): Promise; @@ -64,13 +64,24 @@ export type ConnectionProfile = { loadConnectionProfile(): Promise; /** * Save connection profile - * @param {string} host host url for new profiles or unique substring for existing profiles + * @param {string} host host url for new profiles, unique substring or alias for existing profiles * @returns {Promise} true if the operation succeeded, false otherwise */ saveConnectionProfile(host: string): Promise; + /** + * Set an alias for an existing connection profile + * @param {string} host host url, unique substring, or alias of existing connection profile + * @param {string} alias alias to be assigned to connection profile + */ + setConnectionProfileAlias(host: string, alias: string): void; + /** + * Set an alias for an existing connection profile + * @param {string} host host url, unique substring, or alias of existing connection profile + */ + deleteConnectionProfileAlias(host: string): void; /** * Delete connection profile - * @param {string} host host tenant host url or unique substring + * @param {string} host host tenant, host url, unique substring, or alias */ deleteConnectionProfile(host: string): void; /** @@ -115,6 +126,12 @@ export default (state: State): ConnectionProfile => { async saveConnectionProfile(host: string): Promise { return saveConnectionProfile({ host, state }); }, + setConnectionProfileAlias(host: string, alias: string): void { + setConnectionProfileAlias({ host, alias, state }); + }, + deleteConnectionProfileAlias(host: string): void { + deleteConnectionProfileAlias({ host, state }); + }, deleteConnectionProfile(host: string): void { deleteConnectionProfile({ host, state }); }, @@ -131,6 +148,7 @@ const fileOptions = { export interface SecureConnectionProfileInterface { tenant: string; idmHost?: string; + alias?: string; allowInsecureConnection?: boolean; deploymentType?: string; username?: string | null; @@ -150,6 +168,7 @@ export interface SecureConnectionProfileInterface { export interface ConnectionProfileInterface { tenant: string; idmHost?: string; + alias?: string; allowInsecureConnection?: boolean; deploymentType?: string; username?: string | null; @@ -197,11 +216,11 @@ export function getConnectionProfilesPath({ state }: { state: State }): string { /** * Find connection profiles * @param {ConnectionsFileInterface} connectionProfiles connection profile object - * @param {string} host host url or unique substring + * @param {string} host host url, unique substring, or alias * @param {State} state library state * @returns {SecureConnectionProfileInterface[]} Array of connection profiles */ -function findConnectionProfiles({ +export function findConnectionProfiles({ connectionProfiles, host, state, @@ -211,14 +230,23 @@ function findConnectionProfiles({ state: State; }): SecureConnectionProfileInterface[] { const profiles: SecureConnectionProfileInterface[] = []; + // First check for aliases + for (const tenant in connectionProfiles) { + const profile = connectionProfiles[tenant]; + if (profile.alias === host) { + debugMessage({ + message: `ConnectionProfileOps.findConnectionProfiles: '${host}' matched alias for '${tenant}', including in result set`, + state, + }); + const foundProfile = { ...profile, tenant }; + return [foundProfile]; + } + } + // Then check substring for (const tenant in connectionProfiles) { - // debugMessage({ - // message: `ConnectionProfileOps.findConnectionProfiles: tenant=${tenant}`, - // state, - // }); if (tenant.includes(host)) { debugMessage({ - message: `ConnectionProfileOps.findConnectionProfiles: '${host}' identifies '${tenant}', including in result set`, + message: `ConnectionProfileOps.findConnectionProfiles: '${host}' matched as substring in '${tenant}', including in result set`, state, }); const foundProfile = { ...connectionProfiles[tenant] }; @@ -343,7 +371,7 @@ export async function initConnectionProfiles({ state }: { state: State }) { /** * Get connection profile by host - * @param {string} host host tenant host url or unique substring + * @param {string} host host tenant, host url, unique substring, or alias * @param {State} state library state * @returns {Promise} connection profile */ @@ -383,6 +411,7 @@ export async function getConnectionProfileByHost({ return { tenant: profiles[0].tenant, idmHost: profiles[0].idmHost ? profiles[0].idmHost : null, + alias: profiles[0].alias, allowInsecureConnection: profiles[0].allowInsecureConnection, deploymentType: profiles[0].deploymentType, username: profiles[0].username ? profiles[0].username : null, @@ -429,7 +458,7 @@ export async function getConnectionProfile({ /** * Load a connection profile into library state * @param {Object} params Params object - * @param {string} params.host AM host URL or unique substring + * @param {string} params.host AM host URL, unique substring, or alias * @param {State} params.state State object * @returns {Promise} A promise resolving to true if successful */ @@ -475,7 +504,7 @@ export async function loadConnectionProfile({ /** * Save connection profile - * @param {string} host host url for new profiles or unique substring for existing profiles + * @param {string} host host url for new profiles, unique substring or alias for existing profiles * @returns {Promise} true if the operation succeeded, false otherwise */ export async function saveConnectionProfile({ @@ -544,6 +573,25 @@ export async function saveConnectionProfile({ // idm host if (state.getIdmHost()) profile.idmHost = state.getIdmHost(); + // alias + if (state.getAlias()) { + const alias = state.getAlias(); + findConnectionProfiles({ + connectionProfiles: profiles, + host: alias, + state, + }); + // check for unique alias + for (const profile in profiles) { + if (profiles[profile].alias === alias) { + throw new FrodoError( + `Alias '${alias}' is already in use by connection profile '${profile}'. Please use a unique alias.` + ); + } + } + profile.alias = state.getAlias(); + } + // allow insecure connection if (state.getAllowInsecureConnection()) profile.allowInsecureConnection = state.getAllowInsecureConnection(); @@ -673,9 +721,115 @@ export async function saveConnectionProfile({ } } +/** + * Sets alias for existing connection profile + * @param {string} host host url for new profiles, unique substring or alias for existing profiles + * @param {string} alias alias to be assigned to connection profile + * @param {State} state library state + */ +export function setConnectionProfileAlias({ + host, + alias, + state, +}: { + host: string; + alias: string; + state: State; +}) { + const filename = getConnectionProfilesPath({ state }); + let connectionsData: ConnectionsFileInterface = {}; + if (!fs.statSync(filename, { throwIfNoEntry: false })) { + throw new FrodoError(`Connection profiles file ${filename} not found`); + } + const data = fs.readFileSync(filename, 'utf8'); + connectionsData = JSON.parse(data); + const profiles = findConnectionProfiles({ + connectionProfiles: connectionsData, + host, + state, + }); + if (profiles.length === 0) { + throw new FrodoError(`No connection profile found matching '${host}'`); + } + if (profiles.length > 1) { + throw new FrodoError( + `Multiple matching connection profiles found matching '${host}':\n - ${profiles + .map((profile) => profile.tenant) + .join( + '\n - ' + )}\nSpecify a sub-string uniquely identifying a single connection profile host URL.` + ); + } + const tenant = profiles[0].tenant; + for (const existingTenant in connectionsData) { + if ( + existingTenant !== tenant && + connectionsData[existingTenant].alias === alias + ) { + throw new FrodoError( + `Alias '${alias}' is already in use by connection profile '${existingTenant}'. Please use a unique alias.` + ); + } + } + connectionsData[tenant].alias = alias; + fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2)); + printMessage({ + message: `Alias '${alias}' has been set for connection profile '${tenant}'`, + type: 'info', + state, + }); +} + +/** + * Deletes the alias of an existing connection profile + * @param {string} host unique substring or alias of an existing profile + * @param {State} state library state + */ +export function deleteConnectionProfileAlias({ + host, + state, +}: { + host: string; + state: State; +}) { + const filename = getConnectionProfilesPath({ state }); + if (!fs.statSync(filename, { throwIfNoEntry: false })) { + throw new FrodoError(`Connection profiles file ${filename} not found`); + } + const data = fs.readFileSync(filename, 'utf8'); + const connectionsData: ConnectionsFileInterface = JSON.parse(data); + const profiles = findConnectionProfiles({ + connectionProfiles: connectionsData, + host, + state, + }); + if (profiles.length === 0) { + throw new FrodoError(`No connection profile found matching '${host}'`); + } + if (profiles.length > 1) { + throw new FrodoError( + `Multiple matching connection profiles found matching '${host}':\n - ${profiles + .map((profile) => profile.tenant) + .join('\n - ')}\nUse a more specific identifier or alias.` + ); + } + const tenant = profiles[0].tenant; + if (!connectionsData[tenant].alias) { + throw new FrodoError(`No alias is set for connection profile '${tenant}'`); + } + const alias = connectionsData[tenant].alias; + delete connectionsData[tenant].alias; + fs.writeFileSync(filename, JSON.stringify(connectionsData, null, 2)); + printMessage({ + message: `Alias '${alias}' has been deleted for connection profile '${tenant}'.`, + type: 'info', + state, + }); +} + /** * Delete connection profile - * @param {String} host host tenant host url or unique substring + * @param {String} host host tenant, host url, unique substring, or alias */ export function deleteConnectionProfile({ host, diff --git a/src/shared/State.ts b/src/shared/State.ts index e420f0642..ae1f3501d 100644 --- a/src/shared/State.ts +++ b/src/shared/State.ts @@ -21,7 +21,7 @@ export type State = { getState(): StateInterface; /** * Set the AM host base URL - * @param host Access Management base URL, e.g.: https://cdk.iam.example.com/am. To use a connection profile, just specify a unique substring. + * @param host Access Management base URL, e.g.: https://cdk.iam.example.com/am. To use a connection profile, just specify a unique substring or alias. */ setHost(host: string): void; /** @@ -31,7 +31,7 @@ export type State = { getHost(): string; /** * Set the IDM host base URL - * @param host Identity Management base URL, e.g.: https://cdk.iam.example.com/openidm. To use a connection profile, just specify a unique substring. + * @param host Identity Management base URL, e.g.: https://cdk.iam.example.com/openidm. To use a connection profile, just specify a unique substring or alias. */ setIdmHost(host: string): void; /** @@ -39,6 +39,8 @@ export type State = { * @returns the IDM host base URL */ getIdmHost(): string; + setAlias(alias: string): void; + getAlias(): string | undefined; setUsername(username: string): void; getUsername(): string; setPassword(password: string): void; @@ -194,6 +196,13 @@ export default (initialState: StateInterface): State => { return state.idmHost || process.env.FRODO_IDM_HOST; }, + setAlias(alias: string) { + state.alias = alias; + }, + getAlias() { + return state.alias; + }, + setUsername(username: string) { state.username = username; }, @@ -518,6 +527,7 @@ export interface StateInterface { // connection settings host?: string; idmHost?: string; + alias?: string; username?: string; password?: string; realm?: string; From 84ea6748f6facf577e9dc6fc58727de48a3f1a60 Mon Sep 17 00:00:00 2001 From: Devin Holderness Date: Fri, 19 Dec 2025 09:41:08 -0700 Subject: [PATCH 2/3] Removed dead code --- src/ops/ConnectionProfileOps.test.ts | 1 - src/ops/ConnectionProfileOps.ts | 5 ----- 2 files changed, 6 deletions(-) diff --git a/src/ops/ConnectionProfileOps.test.ts b/src/ops/ConnectionProfileOps.test.ts index 4d540df09..26c48578c 100644 --- a/src/ops/ConnectionProfileOps.test.ts +++ b/src/ops/ConnectionProfileOps.test.ts @@ -11,7 +11,6 @@ import { homedir } from 'os'; import { FrodoError, state } from '../index'; import * as ConnectionProfileOps from './ConnectionProfileOps'; import Constants from '../shared/Constants'; -import { CONNECTOR_TYPE } from './ConnectorOps'; const exampleHost = 'https://openam-tenant-name.forgeblocks.com/am'; const exampleUsername = 'frodo.baggins@shire.me'; diff --git a/src/ops/ConnectionProfileOps.ts b/src/ops/ConnectionProfileOps.ts index 8cf20df57..18c08e7b4 100644 --- a/src/ops/ConnectionProfileOps.ts +++ b/src/ops/ConnectionProfileOps.ts @@ -576,11 +576,6 @@ export async function saveConnectionProfile({ // alias if (state.getAlias()) { const alias = state.getAlias(); - findConnectionProfiles({ - connectionProfiles: profiles, - host: alias, - state, - }); // check for unique alias for (const profile in profiles) { if (profiles[profile].alias === alias) { From d03eba79434a38e34e70f11c4d2dbd931e6e531a Mon Sep 17 00:00:00 2001 From: Devin Holderness Date: Fri, 19 Dec 2025 13:57:31 -0700 Subject: [PATCH 3/3] audit fix --- package-lock.json | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 89e93c2fe..8f9c6aca7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1353,9 +1353,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -7585,9 +7585,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -8234,9 +8234,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", + "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { @@ -9395,15 +9395,15 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -9419,11 +9419,11 @@ } }, "node_modules/rimraf/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -10204,9 +10204,9 @@ } }, "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": {