diff --git a/projects/igniteui-angular/src/lib/grids/common/random.spec.ts b/projects/igniteui-angular/src/lib/grids/common/random.spec.ts new file mode 100644 index 00000000000..05da7a0e090 --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/common/random.spec.ts @@ -0,0 +1,26 @@ +import { getUUID } from './random'; + +describe('Random (crypto.randomUuid()) fallback unit tests', () => { + let originalRandomUuid = crypto.randomUUID; + + beforeAll(() => { + crypto.randomUUID = null; // Mock crypto.randomUUID to simulate a non-secure context + }); + + it('should generate a valid UUID', () => { + const uuid = getUUID(); + expect(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/); + }); + + it('should generate unique UUIDs', () => { + const uuids = new Set(); + for (let i = 0; i < 100; i++) { + uuids.add(getUUID()); + } + expect(uuids.size).toBe(100); // All UUIDs should be unique + }); + + afterAll(() => { + crypto.randomUUID = originalRandomUuid; // Restore the original function + }); +}); \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/grids/common/random.ts b/projects/igniteui-angular/src/lib/grids/common/random.ts new file mode 100644 index 00000000000..4c7d0684653 --- /dev/null +++ b/projects/igniteui-angular/src/lib/grids/common/random.ts @@ -0,0 +1,19 @@ +/** + * Use the function to get a random UUID string when secure context is not guaranteed making crypto.randomUUID unavailable. + * @returns A random UUID string. + */ +export function getUUID(): `${string}-${string}-${string}-${string}-${string}` { + if (typeof crypto.randomUUID === 'function') { + return crypto.randomUUID(); + } + // Secure fallback using crypto.getRandomValues() + const bytes = new Uint8Array(16); + crypto.getRandomValues(bytes); + + // Set version (4) and variant (RFC 4122) + bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4 + bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 1 + + const a = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join(''); + return `${a.slice(0, 8)}-${a.slice(8, 12)}-${a.slice(12, 16)}-${a.slice(16, 20)}-${a.slice(20)}`; +} diff --git a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts index d48448c1f46..31b38405266 100644 --- a/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts +++ b/projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts @@ -1,6 +1,7 @@ import { isTree } from '../../../data-operations/expressions-tree-util'; import { FilteringLogic, IFilteringExpression } from '../../../data-operations/filtering-expression.interface'; import { IFilteringExpressionsTree } from '../../../data-operations/filtering-expressions-tree'; +import { getUUID } from '../../common/random'; /** * @hidden @internal @@ -30,7 +31,7 @@ export class ExpressionUI { constructor() { // Use IDs to identify expressions clearly and use to track them in template @for cycles. - this.expressionId = crypto.randomUUID(); + this.expressionId = getUUID(); } } diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 160253bf11f..4d02cd45be2 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -180,6 +180,7 @@ import { IgxGridCellComponent } from './cell.component'; import { IgxGridValidationService } from './grid/grid-validation.service'; import { getCurrentResourceStrings } from '../core/i18n/resources'; import { isTree, recreateTreeFromFields } from '../data-operations/expressions-tree-util'; +import { getUUID } from './common/random'; interface IMatchInfoCache { row: any; @@ -3787,7 +3788,7 @@ export abstract class IgxGridBaseDirective implements GridType, const primaryColumn = this._columns.find(col => col.field === this.primaryKey); const idType = this.data.length ? this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string'; - return idType === 'string' ? crypto.randomUUID() : FAKE_ROW_ID--; + return idType === 'string' ? getUUID() : FAKE_ROW_ID--; } /**