From 3563d32066d356392bf3e8888eb062308dab0d86 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Mon, 22 Jun 2026 00:42:09 +0000 Subject: [PATCH 1/4] [Performance] Memoize macAddress in context/local.ts The MAC address lookup is an expensive operation that involves enumerating network interfaces. Currently, it is performed every time `macAddress()` is called, which happens at least once per command execution for analytics. By memoizing the `Promise` returned by `macaddress.one()`, we ensure that the lookup logic is executed only once per process. Subsequent calls will return the same promise, avoiding redundant work. Verified with unit tests and full package checks (lint, knip, type-check). --- .../src/public/node/context/local.test.ts | 27 +++++++++++++++++++ .../cli-kit/src/public/node/context/local.ts | 15 ++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/cli-kit/src/public/node/context/local.test.ts b/packages/cli-kit/src/public/node/context/local.test.ts index abe71bb52b7..719ee55cf63 100644 --- a/packages/cli-kit/src/public/node/context/local.test.ts +++ b/packages/cli-kit/src/public/node/context/local.test.ts @@ -8,6 +8,7 @@ import { analyticsDisabled, cloudEnvironment, macAddress, + _resetMacAddress, getThemeKitAccessDomain, opentelemetryDomain, } from './local.js' @@ -207,6 +208,10 @@ describe('analitycsDisabled', () => { }) describe('macAddress', () => { + afterEach(() => { + _resetMacAddress() + }) + test('returns any mac address value', async () => { // When const got = await macAddress() @@ -214,6 +219,28 @@ describe('macAddress', () => { // Then expect(got).not.toBeUndefined() }) + + test('memoizes the mac address', async () => { + // When + const got1 = macAddress() + const got2 = macAddress() + + // Then + expect(got1).toBe(got2) + await expect(got1).resolves.toBe(await got2) + }) + + test('resets the memoized mac address', async () => { + // Given + const got1 = macAddress() + _resetMacAddress() + + // When + const got2 = macAddress() + + // Then + expect(got1).not.toBe(got2) + }) }) describe('cloudEnvironment', () => { diff --git a/packages/cli-kit/src/public/node/context/local.ts b/packages/cli-kit/src/public/node/context/local.ts index ad816399db9..94a5d8ca8d5 100644 --- a/packages/cli-kit/src/public/node/context/local.ts +++ b/packages/cli-kit/src/public/node/context/local.ts @@ -44,6 +44,11 @@ let memoizedIsVerbose: boolean | undefined */ let memoizedIsUnitTest: boolean | undefined +/** + * Memoized value for the mac address. + */ +let memoizedMacAddress: Promise | undefined + /** * Returns true if the CLI is running in debug mode. * @@ -292,7 +297,15 @@ export function ciPlatform( * @returns Mac address. */ export function macAddress(): Promise { - return macaddress.one() + return (memoizedMacAddress ??= macaddress.one()) +} + +/** + * Resets the memoized mac address. + * This is only used for testing purposes. + */ +export function _resetMacAddress(): void { + memoizedMacAddress = undefined } /** From 46e370f998fa6c73d9b4e68f92aead05514e5ed3 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Mon, 22 Jun 2026 00:56:10 +0000 Subject: [PATCH 2/4] [Performance] Memoize macAddress and upgrade hashing to SHA256 - Memoized `macAddress` in `context/local.ts` to avoid repeated expensive system calls. - Upgraded `hashString` and `nonRandomUUID` in `crypto.ts` from SHA-1 to SHA-256 to resolve CodeQL security alerts. - Updated relevant unit tests. --- packages/cli-kit/src/public/node/crypto.test.ts | 2 +- packages/cli-kit/src/public/node/crypto.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli-kit/src/public/node/crypto.test.ts b/packages/cli-kit/src/public/node/crypto.test.ts index 5e6d6565203..07236a7bbee 100644 --- a/packages/cli-kit/src/public/node/crypto.test.ts +++ b/packages/cli-kit/src/public/node/crypto.test.ts @@ -6,7 +6,7 @@ describe('hashString', () => { const hash1 = hashString('hello') const hash2 = hashString('hello') expect(hash1).toEqual(hash2) - expect(hash1).toMatch(/[a-f0-9]{40}/) + expect(hash1).toMatch(/[a-f0-9]{64}/) }) }) diff --git a/packages/cli-kit/src/public/node/crypto.ts b/packages/cli-kit/src/public/node/crypto.ts index 9db4502df07..3f6a1bccb4e 100644 --- a/packages/cli-kit/src/public/node/crypto.ts +++ b/packages/cli-kit/src/public/node/crypto.ts @@ -31,13 +31,13 @@ export function sha256(str: string): Buffer { } /** - * Generate the SHA1 hash of a string. + * Generate the SHA256 hash of a string. * * @param str - The string to hash. - * @returns The SHA1 hash of the string. + * @returns The SHA256 hash of the string. */ export function hashString(str: string): string { - return crypto.createHash('sha1').update(str).digest('hex') + return crypto.createHash('sha256').update(str).digest('hex') } /** @@ -81,7 +81,7 @@ export function nonRandomUUID(subject: string): string { // A fixed namespace UUID const namespace = '6ba7b810-9dad-11d1-80b4-00c04fd430c8' return crypto - .createHash('sha1') + .createHash('sha256') .update(Buffer.from(namespace.replace(/-/g, ''), 'hex')) .update(subject) .digest() From b3b3d8da6580b8369bc7c6c7497d1b0acae4c506 Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Mon, 22 Jun 2026 01:13:24 +0000 Subject: [PATCH 3/4] [Performance] Memoize macAddress and upgrade hashing to SHA256 - Memoized `macAddress` in `context/local.ts` to avoid repeated expensive system calls. - Upgraded `hashString` and `nonRandomUUID` in `crypto.ts` from SHA-1 to SHA-256 to resolve CodeQL security alerts. - Updated relevant unit tests across the codebase that relied on hardcoded SHA-1 hash values. --- packages/app/src/cli/services/generate/extension.test.ts | 2 +- .../cli-kit/src/private/node/session/exchange.test.ts | 2 +- packages/cli-kit/src/public/node/session.test.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/app/src/cli/services/generate/extension.test.ts b/packages/app/src/cli/services/generate/extension.test.ts index 8c97436e93d..ca5aea56a3c 100644 --- a/packages/app/src/cli/services/generate/extension.test.ts +++ b/packages/app/src/cli/services/generate/extension.test.ts @@ -266,7 +266,7 @@ describe('initialize a extension', async () => { name, handle: slugify(name), flavor, - uid: 'ba7c20a9-578d-6fee-8cd2-044af992dabd92d8bbfe', + uid: '37e88e17-2d38-e2a3-4753-3e3066cf549c78f8da9bd0582e899b84617a9c4f5b6e', }) }) }, diff --git a/packages/cli-kit/src/private/node/session/exchange.test.ts b/packages/cli-kit/src/private/node/session/exchange.test.ts index d0f2c95c97e..313fc71cf1f 100644 --- a/packages/cli-kit/src/private/node/session/exchange.test.ts +++ b/packages/cli-kit/src/private/node/session/exchange.test.ts @@ -263,7 +263,7 @@ describe.each(tokenExchangeMethods)( ({tokenExchangeMethod, expectedScopes, expectedApi, expectedErrorName}) => { const automationToken = 'customToken' // Generated from `customToken` using `nonRandomUUID()` - const userId = 'eab16ac4-0690-5fed-9d00-71bd202a3c2b37259a8f' + const userId = '9d5342f1-beb2-14c1-9f5d-deee6b83513ca1cb10bbedbc9f98fb0a0e2544a48032' const grantType = 'urn:ietf:params:oauth:grant-type:token-exchange' const accessTokenType = 'urn:ietf:params:oauth:token-type:access_token' diff --git a/packages/cli-kit/src/public/node/session.test.ts b/packages/cli-kit/src/public/node/session.test.ts index 76b1e130ec1..1d1e261fdb1 100644 --- a/packages/cli-kit/src/public/node/session.test.ts +++ b/packages/cli-kit/src/public/node/session.test.ts @@ -63,7 +63,7 @@ describe('ensureAuthenticatedStorefront', () => { // Then expect(got).toEqual('theme_access_password') expect(setLastSeenAuthMethod).toBeCalledWith('custom_app_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('dd5e7850-e2de-d283-9c5f-79c8190a19d18b52e0ce') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith('21534e73-fdc5-9bd3-8c9f-3f45815510a727d0dc9422919e2331006b44fe1777d5') }) test('returns the password if provided, and auth method is theme_access_token', async () => { @@ -73,7 +73,7 @@ describe('ensureAuthenticatedStorefront', () => { // Then expect(got).toEqual('shptka_theme_access_password') expect(setLastSeenAuthMethod).toBeCalledWith('theme_access_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('730a64df-ab2c-3d92-8b11-76a66aadee947aa5c1ce') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith('b7d6d99f-3f60-301f-71b8-3108eacc993ecc1ed720b54a20e766c21df6688e35e6') }) test('throws error if there is no storefront token', async () => { @@ -190,7 +190,7 @@ describe('ensureAuthenticatedTheme', () => { // Then expect(got).toEqual({token: 'password', storeFqdn: 'mystore.myshopify.com'}) expect(setLastSeenAuthMethod).toBeCalledWith('custom_app_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('f5c7086f-320b-3b93-bcdc-a2296adbec02d71eb733') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith('18a8698d-f12b-f2db-4737-cecd09bb2c1eee00fef518e9b94bb14925ecaf132466') }) test('returns the password when is provided and theme_access_token', async () => { @@ -200,7 +200,7 @@ describe('ensureAuthenticatedTheme', () => { // Then expect(got).toEqual({token: 'shptka_password', storeFqdn: 'mystore.myshopify.com'}) expect(setLastSeenAuthMethod).toBeCalledWith('theme_access_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('e3d08cca-4e68-504a-00ec-23e2cea12a6340bb257b') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith('aea5e074-48e7-cb2a-4b3b-6cebbb5d6f26899d8d41455e975048162bcf45a4d4bf') }) }) From 8abd008365366d553e1eb2ca89cdd3253b8d7d6b Mon Sep 17 00:00:00 2001 From: gonzaloriestra <14979109+gonzaloriestra@users.noreply.github.com> Date: Mon, 22 Jun 2026 01:31:34 +0000 Subject: [PATCH 4/4] [Performance] Memoize macAddress and upgrade hashing to SHA256 - Memoized `macAddress` in `context/local.ts` to avoid repeated expensive system calls. - Upgraded `hashString` and `nonRandomUUID` in `crypto.ts` from SHA-1 to SHA-256 to resolve CodeQL security alerts. - Updated relevant unit tests across the codebase that relied on hardcoded SHA-1 hash values. - Fixed linting issues introduced by long SHA-256 strings in tests. --- packages/cli-kit/src/public/node/session.test.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/cli-kit/src/public/node/session.test.ts b/packages/cli-kit/src/public/node/session.test.ts index 1d1e261fdb1..441365824d3 100644 --- a/packages/cli-kit/src/public/node/session.test.ts +++ b/packages/cli-kit/src/public/node/session.test.ts @@ -63,7 +63,9 @@ describe('ensureAuthenticatedStorefront', () => { // Then expect(got).toEqual('theme_access_password') expect(setLastSeenAuthMethod).toBeCalledWith('custom_app_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('21534e73-fdc5-9bd3-8c9f-3f45815510a727d0dc9422919e2331006b44fe1777d5') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith( + '21534e73-fdc5-9bd3-8c9f-3f45815510a727d0dc9422919e2331006b44fe1777d5', + ) }) test('returns the password if provided, and auth method is theme_access_token', async () => { @@ -73,7 +75,9 @@ describe('ensureAuthenticatedStorefront', () => { // Then expect(got).toEqual('shptka_theme_access_password') expect(setLastSeenAuthMethod).toBeCalledWith('theme_access_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('b7d6d99f-3f60-301f-71b8-3108eacc993ecc1ed720b54a20e766c21df6688e35e6') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith( + 'b7d6d99f-3f60-301f-71b8-3108eacc993ecc1ed720b54a20e766c21df6688e35e6', + ) }) test('throws error if there is no storefront token', async () => { @@ -190,7 +194,9 @@ describe('ensureAuthenticatedTheme', () => { // Then expect(got).toEqual({token: 'password', storeFqdn: 'mystore.myshopify.com'}) expect(setLastSeenAuthMethod).toBeCalledWith('custom_app_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('18a8698d-f12b-f2db-4737-cecd09bb2c1eee00fef518e9b94bb14925ecaf132466') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith( + '18a8698d-f12b-f2db-4737-cecd09bb2c1eee00fef518e9b94bb14925ecaf132466', + ) }) test('returns the password when is provided and theme_access_token', async () => { @@ -200,7 +206,9 @@ describe('ensureAuthenticatedTheme', () => { // Then expect(got).toEqual({token: 'shptka_password', storeFqdn: 'mystore.myshopify.com'}) expect(setLastSeenAuthMethod).toBeCalledWith('theme_access_token') - expect(setLastSeenUserIdAfterAuth).toBeCalledWith('aea5e074-48e7-cb2a-4b3b-6cebbb5d6f26899d8d41455e975048162bcf45a4d4bf') + expect(setLastSeenUserIdAfterAuth).toBeCalledWith( + 'aea5e074-48e7-cb2a-4b3b-6cebbb5d6f26899d8d41455e975048162bcf45a4d4bf', + ) }) })