Ran pnpm oxfmt . This should get the web repo aligned so that we can better enforce oxfmt going forward.
75 lines
2.4 KiB
TypeScript
75 lines
2.4 KiB
TypeScript
import type * as Protobuf from "@meshtastic/protobufs";
|
|
import type { Node } from "../domain/Node.ts";
|
|
import type { NodeErrorType } from "../domain/NodeError.ts";
|
|
|
|
/**
|
|
* Byte-equal compare for two public keys. Empty / undefined keys never
|
|
* match each other.
|
|
*/
|
|
export function equalKey(
|
|
a?: Uint8Array | null,
|
|
b?: Uint8Array | null,
|
|
): boolean {
|
|
if (!a || !b) return false;
|
|
if (a === b) return true;
|
|
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 interface ValidatedNodeInfo {
|
|
/** The NodeInfo to commit to the store. `undefined` when the incoming node should be dropped entirely. */
|
|
readonly accepted: Protobuf.Mesh.NodeInfo | undefined;
|
|
/** Optional error to record against the incoming node's number. */
|
|
readonly error: NodeErrorType | undefined;
|
|
}
|
|
|
|
/**
|
|
* Validates an incoming NodeInfo against the existing snapshot of nodes.
|
|
* Pure — no side effects. Caller decides what to do with the verdict.
|
|
*
|
|
* Mirrors the previous web-side validation in
|
|
* `packages/web/src/core/stores/nodeDBStore/nodeValidation.ts` but returns
|
|
* the verdict instead of pushing into a Zustand store.
|
|
*/
|
|
export function validateIncomingNode(
|
|
newNode: Protobuf.Mesh.NodeInfo,
|
|
existingByNum: ReadonlyMap<number, Node>,
|
|
): ValidatedNodeInfo {
|
|
const num = newNode.num;
|
|
const existing = existingByNum.get(num);
|
|
const newKey = newNode.user?.publicKey;
|
|
|
|
if (!existing) {
|
|
if (newKey && newKey.length > 0) {
|
|
// Reject if another node already presents this public key —
|
|
// potential impersonation.
|
|
for (const node of existingByNum.values()) {
|
|
const otherKey = node.user?.publicKey;
|
|
if (otherKey && otherKey.length > 0 && equalKey(otherKey, newKey)) {
|
|
return { accepted: undefined, error: "DUPLICATE_PKI" };
|
|
}
|
|
}
|
|
}
|
|
return { accepted: newNode, error: undefined };
|
|
}
|
|
|
|
const oldKey = existing.user?.publicKey;
|
|
const oldKeyEmpty = !oldKey || oldKey.length === 0;
|
|
|
|
if (oldKeyEmpty || equalKey(oldKey, newKey)) {
|
|
// Trust the incoming update.
|
|
return { accepted: newNode, error: undefined };
|
|
}
|
|
|
|
if (newKey && newKey.length > 0) {
|
|
// Existing node has a key, incoming has a different key — mismatch.
|
|
return { accepted: undefined, error: "MISMATCH_PKI" };
|
|
}
|
|
|
|
// Incoming has no key, existing does — silently drop the update.
|
|
return { accepted: undefined, error: undefined };
|
|
}
|