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:
@@ -79,18 +79,26 @@ export function clearDeviceLabel(mac: string): Promise<{ ok: true }> {
|
||||
return request(`/api/devices/${encodeURIComponent(mac)}/label`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export interface FingerbankResult {
|
||||
deviceName: string | null;
|
||||
score: number | null;
|
||||
version: string | null;
|
||||
}
|
||||
|
||||
export interface DeepCheckResult {
|
||||
ip: string;
|
||||
mdnsHostname: string | null;
|
||||
reverseDns: string | null;
|
||||
ssdp: {
|
||||
location: string;
|
||||
server: string | null;
|
||||
friendlyName: string | null;
|
||||
manufacturer: string | null;
|
||||
modelName: string | null;
|
||||
} | null;
|
||||
openPorts: number[];
|
||||
http: { port: number; title: string | null; server: string | null }[];
|
||||
fingerbank: FingerbankResult | null;
|
||||
}
|
||||
|
||||
export function deepCheckDevice(ip: string): Promise<DeepCheckResult> {
|
||||
|
||||
@@ -90,9 +90,23 @@ function NameCell({ device, onDeviceChanged }: { device: Device; onDeviceChanged
|
||||
);
|
||||
}
|
||||
|
||||
// Bands per Fingerbank's own docs: below 30 "very little confidence", 31-50
|
||||
// moderate, 51-75 high, 76+ very high. Shown alongside the result so a
|
||||
// manufacturer-only guess isn't mistaken for a confirmed identification.
|
||||
function confidenceLabel(score: number): string {
|
||||
if (score < 30) return "low confidence";
|
||||
if (score <= 50) return "moderate confidence";
|
||||
if (score <= 75) return "high confidence";
|
||||
return "very high confidence";
|
||||
}
|
||||
|
||||
function DeepCheckResultPanel({ result }: { result: DeepCheckResult }) {
|
||||
const nothingFound =
|
||||
!result.mdnsHostname && !result.reverseDns && !result.ssdp && result.openPorts.length === 0;
|
||||
!result.mdnsHostname &&
|
||||
!result.reverseDns &&
|
||||
!result.ssdp &&
|
||||
result.openPorts.length === 0 &&
|
||||
!result.fingerbank?.deviceName;
|
||||
|
||||
if (nothingFound) {
|
||||
return <p className="deep-check-empty">No additional information found for {result.ip}.</p>;
|
||||
@@ -100,6 +114,18 @@ function DeepCheckResultPanel({ result }: { result: DeepCheckResult }) {
|
||||
|
||||
return (
|
||||
<dl className="deep-check-results">
|
||||
{result.fingerbank?.deviceName && (
|
||||
<>
|
||||
<dt>Fingerbank ID</dt>
|
||||
<dd>
|
||||
{result.fingerbank.deviceName}
|
||||
{result.fingerbank.version && ` (${result.fingerbank.version})`}
|
||||
{result.fingerbank.score !== null && (
|
||||
<span className="name-hint"> — {confidenceLabel(result.fingerbank.score)}</span>
|
||||
)}
|
||||
</dd>
|
||||
</>
|
||||
)}
|
||||
{result.mdnsHostname && (
|
||||
<>
|
||||
<dt>mDNS hostname</dt>
|
||||
|
||||
Reference in New Issue
Block a user