diff --git a/.changeset/sweet-apples-camp.md b/.changeset/sweet-apples-camp.md new file mode 100644 index 000000000..801362bd5 --- /dev/null +++ b/.changeset/sweet-apples-camp.md @@ -0,0 +1,6 @@ +--- +'@rock-js/tools': patch +'rock': patch +--- + +fix: properly take env value into account; hash sensitive data diff --git a/packages/cli/src/lib/plugins/fingerprint.ts b/packages/cli/src/lib/plugins/fingerprint.ts index ed362839d..40a01760c 100644 --- a/packages/cli/src/lib/plugins/fingerprint.ts +++ b/packages/cli/src/lib/plugins/fingerprint.ts @@ -1,6 +1,7 @@ +import { createHash } from 'node:crypto'; import { performance } from 'node:perf_hooks'; import type { PluginApi } from '@rock-js/config'; -import type { FingerprintSources } from '@rock-js/tools'; +import type { FingerprintInputHash, FingerprintSources } from '@rock-js/tools'; import { color, intro, @@ -12,6 +13,25 @@ import { spinner, } from '@rock-js/tools'; +const hashValue = (value: string) => + `[HASHED:${createHash('sha256').update(value).digest('hex').substring(0, 8)}]`; + +/** + * Redacts sensitive environment variables from fingerprint sources by hashing their values + */ +function redactSensitiveSources(sources: FingerprintInputHash[]) { + return sources.map((source) => { + if (source.key === 'json:env' && 'json' in source) { + const env = source.json as Record; + const redactedEnv = Object.fromEntries( + Object.entries(env).map(([key, value]) => [key, hashValue(value)]), + ); + return { ...source, json: redactedEnv }; + } + return source; + }); +} + type NativeFingerprintCommandOptions = { platform: 'ios' | 'android'; raw?: boolean; @@ -39,7 +59,9 @@ export async function nativeFingerprintCommand( JSON.stringify( { hash: fingerprint.hash, - sources: fingerprint.inputs.filter((source) => source.hash != null), + sources: redactSensitiveSources( + fingerprint.inputs.filter((source) => source.hash != null), + ), }, null, 2, @@ -69,7 +91,9 @@ export async function nativeFingerprintCommand( logger.debug( 'Sources:', JSON.stringify( - fingerprint.inputs.filter((source) => source.hash != null), + redactSensitiveSources( + fingerprint.inputs.filter((source) => source.hash != null), + ), null, 2, ), diff --git a/packages/tools/src/lib/fingerprint/index.ts b/packages/tools/src/lib/fingerprint/index.ts index a9fde73d0..43d46a8a6 100644 --- a/packages/tools/src/lib/fingerprint/index.ts +++ b/packages/tools/src/lib/fingerprint/index.ts @@ -7,6 +7,7 @@ import { getDefaultIgnorePaths, getPlatformDirIgnorePaths, } from './ignorePaths.js'; +export type { FingerprintInputHash } from 'fs-fingerprint'; export type FingerprintSources = { extraSources: string[]; @@ -61,6 +62,15 @@ export async function nativeFingerprint( throw new Error('No platforms found in autolinking project config'); } + let env = undefined; + + if (options.env.length > 0) { + env = options.env.reduce((acc: Record, key: string) => { + acc[key] = process.env[key] ?? ''; + return acc; + }, {}); + } + const fingerprint = await calculateFingerprint(projectRoot, { ignoreFilePath: '.gitignore', include: [ @@ -79,7 +89,7 @@ export async function nativeFingerprint( key: 'reactNativeVersion', json: { version: getReactNativeVersion(projectRoot) }, }, - ...(options.env.length > 0 ? [{ key: 'env', json: options.env }] : []), + ...(env ? [{ key: 'env', json: env }] : []), ], exclude: [ ...getDefaultIgnorePaths(),