Skip to content

Commit a9a7672

Browse files
committed
fix(3742): add validation to metametricsId
1 parent 8724cc3 commit a9a7672

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

packages/remote-feature-flag-controller/src/utils/user-segmentation-utils.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ const MOCK_METRICS_IDS = {
1313
MOBILE_MAX: 'ffffffff-ffff-4fff-bfff-ffffffffffff',
1414
EXTENSION_MIN: `0x${'0'.repeat(64) as string}`,
1515
EXTENSION_MAX: `0x${'f'.repeat(64) as string}`,
16+
UUID_V3: '00000000-0000-3000-8000-000000000000',
17+
INVALID_HEX_NO_PREFIX: '86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d136420',
18+
INVALID_HEX_SHORT: '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d13642',
19+
INVALID_HEX_LONG: '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d1364200',
20+
INVALID_HEX_INVALID_CHARS: '0x86bacb9b2bf9a7e8d2b147eadb95ac9aaa26842327cd24afc8bd4b3c1d13642g',
1621
};
1722

1823
const MOCK_FEATURE_FLAGS = {
@@ -125,6 +130,29 @@ describe('user-segmentation-utils', () => {
125130
});
126131
});
127132
});
133+
134+
describe('MetaMetrics ID validation', () => {
135+
it('throws an error if the MetaMetrics ID is not a valid UUIDv4', () => {
136+
expect(() => generateDeterministicRandomNumber(MOCK_METRICS_IDS.UUID_V3)).toThrow('Invalid UUID version. Expected v4, got v3');
137+
});
138+
139+
it('throws an error if the MetaMetrics ID is not a valid hex string', () => {
140+
expect(() => generateDeterministicRandomNumber(MOCK_METRICS_IDS.INVALID_HEX_NO_PREFIX)).toThrow('Hex ID must start with 0x prefix');
141+
});
142+
143+
it('throws an error if the MetaMetrics ID is not a valid hex string', () => {
144+
expect(() => generateDeterministicRandomNumber(MOCK_METRICS_IDS.INVALID_HEX_SHORT)).toThrow('Invalid hex ID length. Expected 64 characters, got 63');
145+
});
146+
147+
it('throws an error if the MetaMetrics ID is not a valid hex string', () => {
148+
expect(() => generateDeterministicRandomNumber(MOCK_METRICS_IDS.INVALID_HEX_LONG)).toThrow('Invalid hex ID length. Expected 64 characters, got 65');
149+
});
150+
151+
it('throws an error if the MetaMetrics ID contains invalid hex characters', () => {
152+
expect(() => generateDeterministicRandomNumber(MOCK_METRICS_IDS.INVALID_HEX_INVALID_CHARS))
153+
.toThrow('Hex ID contains invalid characters');
154+
});
155+
});
128156
});
129157

130158
describe('isFeatureFlagWithScopeValue', () => {

packages/remote-feature-flag-controller/src/utils/user-segmentation-utils.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,43 @@ const UUID_V4_VALUE_RANGE_BIGINT =
3232
export function generateDeterministicRandomNumber(
3333
metaMetricsId: string,
3434
): number {
35+
if (!metaMetricsId) {
36+
throw new Error('MetaMetrics ID cannot be empty');
37+
}
38+
3539
let idValue: bigint;
3640
let maxValue: bigint;
41+
3742
// uuidv4 format
38-
if (uuidValidate(metaMetricsId) && uuidVersion(metaMetricsId) === 4) {
39-
// Normalize the UUIDv4 range to start from 0 by subtracting MIN_UUID_V4_BIGINT.
40-
// This ensures uniform distribution across the entire range, since UUIDv4
41-
// has restricted bits for version (4) and variant (8-b) that would otherwise skew the distribution
43+
if (uuidValidate(metaMetricsId)) {
44+
if (uuidVersion(metaMetricsId) !== 4) {
45+
throw new Error(`Invalid UUID version. Expected v4, got v${uuidVersion(metaMetricsId)}`);
46+
}
4247
idValue = uuidStringToBigInt(metaMetricsId) - MIN_UUID_V4_BIGINT;
4348
maxValue = UUID_V4_VALUE_RANGE_BIGINT;
4449
} else {
4550
// hex format with 0x prefix
51+
if (!metaMetricsId.startsWith('0x')) {
52+
throw new Error('Hex ID must start with 0x prefix');
53+
}
54+
4655
const cleanId = metaMetricsId.slice(2);
56+
const EXPECTED_HEX_LENGTH = 64; // 32 bytes = 64 hex characters
57+
58+
if (cleanId.length !== EXPECTED_HEX_LENGTH) {
59+
throw new Error(
60+
`Invalid hex ID length. Expected ${EXPECTED_HEX_LENGTH} characters, got ${cleanId.length}`
61+
);
62+
}
63+
64+
if (!/^[0-9a-f]+$/i.test(cleanId)) {
65+
throw new Error('Hex ID contains invalid characters');
66+
}
67+
4768
idValue = BigInt(`0x${cleanId}`);
4869
maxValue = BigInt(`0x${'f'.repeat(cleanId.length)}`);
4970
}
71+
5072
// Use BigInt division first, then convert to number to maintain precision
5173
return Number((idValue * BigInt(1_000_000)) / maxValue) / 1_000_000;
5274
}

0 commit comments

Comments
 (0)