mirror of
https://github.com/meshtastic/web.git
synced 2025-12-24 00:00:01 -05:00
Fix admin PKI validation (#766)
Admin PKI fields were falsely flagged as unchanged. Co-authored-by: philon- <philon-@users.noreply.github.com>
This commit is contained in:
@@ -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,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,22 @@ function isObject(value: unknown): value is Record<string, unknown> {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user