Add optional Fingerbank device fingerprinting to deep-check
Folded into the existing on-demand Deep check button: queries Fingerbank's interrogate API with the device's MAC plus the SSDP SERVER header when deep-check-device.sh finds one, showing the confidence band alongside the result. Runs directly from the API container (no host-level access needed, just an outbound HTTPS call), unlike the SSDP/mDNS steps. Confirmed via direct testing: without DHCP fingerprint data (which we structurally don't have, not being the DHCP server), MAC-only queries often can't get past manufacturer-level confidence -- same info the free OUI lookup already provides. Documented honestly in docs/device-discovery.md rather than overselling it. Still worth having as opt-in enrichment for devices that do expose richer signals. Gated behind optional FINGERBANK_API_KEY -- missing key, API errors, or no match all degrade gracefully without affecting the rest of deep-check's local findings. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
export interface FingerbankResult {
|
||||
deviceName: string | null;
|
||||
score: number | null;
|
||||
version: string | null;
|
||||
}
|
||||
|
||||
export interface FingerbankSignals {
|
||||
mac: string;
|
||||
// "Upnp string" per Fingerbank's docs -- we pass the SSDP SERVER header
|
||||
// captured by deep-check-device.sh, when present (e.g.
|
||||
// "Linux/3.10 UPnP/1.0 MiniDLNA/1.1"), as extra fingerprinting signal
|
||||
// beyond the bare MAC.
|
||||
upnpServer: string | null;
|
||||
}
|
||||
|
||||
// On-demand only (folded into the existing deep-check button), never
|
||||
// automatic/bulk -- keeps this well under Fingerbank's free-tier query
|
||||
// limits and avoids sending every device's MAC to a third party by default.
|
||||
export async function identifyDevice(
|
||||
apiKey: string,
|
||||
signals: FingerbankSignals
|
||||
): Promise<FingerbankResult | null> {
|
||||
const body: Record<string, unknown> = { mac: signals.mac };
|
||||
if (signals.upnpServer) body.upnp_user_agents = [signals.upnpServer];
|
||||
|
||||
const res = await fetch("https://api.fingerbank.org/api/v2/combinations/interrogate", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (res.status === 404) {
|
||||
// No match in Fingerbank's database -- not an error, just nothing found.
|
||||
return null;
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(`Fingerbank API returned ${res.status}`);
|
||||
}
|
||||
|
||||
const data = (await res.json()) as {
|
||||
device_name?: string;
|
||||
score?: number;
|
||||
version?: string;
|
||||
};
|
||||
|
||||
return {
|
||||
deviceName: data.device_name ?? null,
|
||||
score: data.score ?? null,
|
||||
version: data.version ?? null,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user