diff --git a/packages/web/src/components/PageComponents/Config/Security/Security.tsx b/packages/web/src/components/PageComponents/Config/Security/Security.tsx index abdd7377..d47e8bf7 100644 --- a/packages/web/src/components/PageComponents/Config/Security/Security.tsx +++ b/packages/web/src/components/PageComponents/Config/Security/Security.tsx @@ -197,14 +197,12 @@ export const Security = ({ onFormInit }: SecurityConfigProps) => { description: t("security.primaryAdminKey.description"), bits, devicePSKBitCount: 32, - hide: true, actionButtons: [], disabledBy: [ { fieldName: "adminChannelEnabled", invert: true }, ], properties: { showCopyButton: true, - showPasswordToggle: true, }, }, { @@ -215,14 +213,12 @@ export const Security = ({ onFormInit }: SecurityConfigProps) => { description: t("security.secondaryAdminKey.description"), bits, devicePSKBitCount: 32, - hide: true, actionButtons: [], disabledBy: [ { fieldName: "adminChannelEnabled", invert: true }, ], properties: { showCopyButton: true, - showPasswordToggle: true, }, }, { @@ -233,14 +229,12 @@ export const Security = ({ onFormInit }: SecurityConfigProps) => { description: t("security.tertiaryAdminKey.description"), bits, devicePSKBitCount: 32, - hide: true, actionButtons: [], disabledBy: [ { fieldName: "adminChannelEnabled", invert: true }, ], properties: { showCopyButton: true, - showPasswordToggle: true, }, }, { diff --git a/packages/web/src/core/utils/deepCompareConfig.test.ts b/packages/web/src/core/utils/deepCompareConfig.test.ts index b504d2b7..db664f41 100644 --- a/packages/web/src/core/utils/deepCompareConfig.test.ts +++ b/packages/web/src/core/utils/deepCompareConfig.test.ts @@ -57,4 +57,49 @@ describe("deepCompareConfig", () => { expect(deepCompareConfig([1, 2, 3, 4], [1, 2], true)).toBe(true); expect(deepCompareConfig([1, 2, 3, 4], [1, 2], false)).toBe(false); }); + + it("compares Uint8Array strictly: equal bytes -> true", () => { + const a = new Uint8Array([1, 2, 3]); + const b = new Uint8Array([1, 2, 3]); + expect(deepCompareConfig(a, b)).toBe(true); + }); + + it("compares Uint8Array strictly: different bytes -> false", () => { + const a = new Uint8Array([1, 2, 3]); + const b = new Uint8Array([1, 2, 4]); + expect(deepCompareConfig(a, b)).toBe(false); + }); + + it("Uint8Array vs undefined is false even when allowUndefined is true", () => { + const a = new Uint8Array([1, 2, 3]); + expect(deepCompareConfig(a, undefined, true)).toBe(false); + expect(deepCompareConfig(undefined, a, true)).toBe(false); + }); + + it("nested Uint8Array fields must match exactly", () => { + const existing = { data: new Uint8Array([9, 8, 7]) }; + const workingEqual = { data: new Uint8Array([9, 8, 7]) }; + const workingDiff = { data: new Uint8Array([9, 8, 6]) }; + const workingUndef = { data: undefined as unknown }; + + expect(deepCompareConfig(existing, workingEqual)).toBe(true); + expect(deepCompareConfig(existing, workingDiff)).toBe(false); + // still false even with allowUndefined + expect(deepCompareConfig(existing, workingUndef, true)).toBe(false); + }); + + it("arrays containing Uint8Array: element must match exactly", () => { + const a = [new Uint8Array([1, 2]), new Uint8Array([3, 4])]; + const b = [new Uint8Array([1, 2]), new Uint8Array([3, 4])]; + const c = [new Uint8Array([1, 2]), new Uint8Array([3, 9])]; + + expect(deepCompareConfig(a, b)).toBe(true); + expect(deepCompareConfig(a, c)).toBe(false); + }); + + it("shorter working array with missing Uint8Array element -> false even with allowUndefined", () => { + const existing = [new Uint8Array([1, 2]), new Uint8Array([3, 4])]; + const workingShort = [new Uint8Array([1, 2])]; // missing the second byte array + expect(deepCompareConfig(existing, workingShort, true)).toBe(false); + }); }); diff --git a/packages/web/src/core/utils/deepCompareConfig.ts b/packages/web/src/core/utils/deepCompareConfig.ts index aca12452..c1f5f5f1 100644 --- a/packages/web/src/core/utils/deepCompareConfig.ts +++ b/packages/web/src/core/utils/deepCompareConfig.ts @@ -2,6 +2,22 @@ function isObject(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } +function isUint8Array(v: unknown): v is Uint8Array { + return v instanceof Uint8Array; +} + +function bytesEqual(a: Uint8Array, b: Uint8Array): boolean { + if (a.byteLength !== b.byteLength) { + return false; + } + for (let i = 0; i < a.byteLength; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} + export function deepCompareConfig( a: unknown, b: unknown, @@ -11,7 +27,10 @@ export function deepCompareConfig( return true; } - // If allowUndefined is true, and one is undefined, they are considered equal. // This check is placed early to simplify subsequent logic. + if (isUint8Array(a) || isUint8Array(b)) { + return isUint8Array(a) && isUint8Array(b) && bytesEqual(a, b); + } + if (allowUndefined && (a === undefined || b === undefined)) { return true; }