The 662-line handler (the largest in the file) split into one function per data-source/merge stage: facility+IX-location coordinate resolution (facilities.js), neighbour name-resolution + threat-intel enrichment (enrich-neighbours.js), RIR/country derivation cascade (rir-country.js), visibility/prefix-size computation (routing-info.js), multi-source cross-checks + data-quality scoring (cross-check.js), and a slim orchestrator (index.js) running Phase 0/1 fetch and final result assembly. Verified via node --check, eslint no-undef (2 known bugs remain: compare, webhooks), and smoke-test harness (28/28 match, including a byte-for-byte match on /api/lookup's own large response body -- the strongest possible confirmation this, the most complex handler in the file, was decomposed without changing behavior). server.js: 2917 -> 1734 lines (6838 originally -- 75% reduction so far).
91 lines
5.3 KiB
JavaScript
91 lines
5.3 KiB
JavaScript
const { crossCheckRpki } = require("../../services/rpki");
|
|
|
|
// Multi-source cross-checks (RPKI vs RIPE Validator, prefix/neighbour counts
|
|
// vs bgp.he.net) plus the overall data-quality summary derived from them.
|
|
async function computeCrossChecks(asn, allPrefixes, rpkiStatuses, prefixes, neighbours, bgpHeData) {
|
|
let rpkiCrossCheck = { cloudflare_valid: 0, ripe_valid: 0, agreement_pct: null, disagreements: [], sample_size: 0, comparable_size: 0 };
|
|
let prefixCrossCheck = { ripe_stat: prefixes.length, bgp_he_net: null, agreement: null, note: "" };
|
|
let neighbourCrossCheck = { ripe_stat_total: neighbours.length, bgp_he_net_total: null };
|
|
|
|
try {
|
|
// RPKI cross-check: sample up to 5 prefixes against RIPE Validator (with 8s total timeout)
|
|
const rpkiCrossPromise = crossCheckRpki(asn, allPrefixes, rpkiStatuses);
|
|
const rpkiCrossResult = await Promise.race([
|
|
rpkiCrossPromise,
|
|
new Promise((resolve) => setTimeout(() => resolve(null), 8000)),
|
|
]);
|
|
if (rpkiCrossResult) rpkiCrossCheck = rpkiCrossResult;
|
|
} catch (_e) { /* cross-check failed, keep defaults */ }
|
|
|
|
// bgp.he.net fetching is deliberately disabled (see the hardcoded
|
|
// Promise.resolve(null) in index.js's timedFetch("bgp.he.net", ...)) --
|
|
// bgpHeData is therefore always null and this whole branch, along with the
|
|
// prefix/neighbour cross-checks below, never actually runs. Left in place
|
|
// (not deleted) in case bgp.he.net fetching is re-enabled later, but
|
|
// overall_confidence below must not claim a 2-source agreement that never
|
|
// happened -- see the sources:1/2 handling further down.
|
|
// Prefix count cross-check: compare RIPE Stat vs bgp.he.net
|
|
if (bgpHeData) {
|
|
const heV4 = bgpHeData.prefixes_v4 || 0;
|
|
const heV6 = bgpHeData.prefixes_v6 || 0;
|
|
const heTotal = heV4 + heV6;
|
|
if (heTotal > 0) {
|
|
prefixCrossCheck.bgp_he_net = heTotal;
|
|
const ripeStat = prefixes.length;
|
|
if (ripeStat > 0 && heTotal > 0) {
|
|
const ratio = Math.min(ripeStat, heTotal) / Math.max(ripeStat, heTotal);
|
|
prefixCrossCheck.agreement = ratio >= 0.9;
|
|
const diff = Math.abs(ripeStat - heTotal);
|
|
prefixCrossCheck.note = diff === 0
|
|
? "Exact match"
|
|
: "Difference of " + diff + " prefixes (" + Math.round((1 - ratio) * 100) + "% divergence)";
|
|
}
|
|
} else {
|
|
prefixCrossCheck.note = "bgp.he.net prefix count unavailable";
|
|
}
|
|
|
|
// Neighbour cross-check: compare RIPE Stat vs bgp.he.net peer_count
|
|
if (bgpHeData.peer_count != null) {
|
|
neighbourCrossCheck.bgp_he_net_total = bgpHeData.peer_count;
|
|
}
|
|
} else {
|
|
prefixCrossCheck.note = "bgp.he.net data unavailable";
|
|
}
|
|
|
|
// Compute overall data quality. Only push a score for a cross-check that
|
|
// actually ran (agreement_pct !== null) -- an unrun/inconclusive check must
|
|
// not silently count as a perfect 100 in the average, and confidence must
|
|
// reflect "nothing was actually cross-checked" rather than defaulting high.
|
|
const crossCheckScores = [];
|
|
if (rpkiCrossCheck.agreement_pct !== null) crossCheckScores.push(rpkiCrossCheck.agreement_pct);
|
|
// Prefix agreement: convert to percentage
|
|
if (prefixCrossCheck.bgp_he_net != null && prefixes.length > 0) {
|
|
const pfxRatio = Math.min(prefixes.length, prefixCrossCheck.bgp_he_net) / Math.max(prefixes.length, prefixCrossCheck.bgp_he_net);
|
|
crossCheckScores.push(Math.round(pfxRatio * 100));
|
|
}
|
|
// Neighbour agreement
|
|
if (neighbourCrossCheck.bgp_he_net_total != null && neighbours.length > 0) {
|
|
const nbrRatio = Math.min(neighbours.length, neighbourCrossCheck.bgp_he_net_total) / Math.max(neighbours.length, neighbourCrossCheck.bgp_he_net_total);
|
|
crossCheckScores.push(Math.round(nbrRatio * 100));
|
|
}
|
|
const avgAgreement = crossCheckScores.length > 0
|
|
? Math.round(crossCheckScores.reduce((a, b) => a + b, 0) / crossCheckScores.length)
|
|
: null;
|
|
const overallConfidence = avgAgreement === null ? "unknown" : avgAgreement > 90 ? "high" : avgAgreement >= 70 ? "medium" : "low";
|
|
|
|
const dataQuality = {
|
|
sources_queried: ["PeeringDB", "RIPE Stat", "bgp.he.net", "Cloudflare RPKI", "RIPE RPKI Validator"],
|
|
cross_checks: {
|
|
rpki: { sources: rpkiCrossCheck.agreement_pct !== null ? 2 : 1, agreement_pct: rpkiCrossCheck.agreement_pct, sample_size: rpkiCrossCheck.sample_size, comparable_size: rpkiCrossCheck.comparable_size, disagreements: rpkiCrossCheck.disagreements },
|
|
prefixes: { sources: prefixCrossCheck.bgp_he_net != null ? 2 : 1, agreement_pct: prefixCrossCheck.bgp_he_net != null ? Math.round((Math.min(prefixes.length, prefixCrossCheck.bgp_he_net) / Math.max(prefixes.length, prefixCrossCheck.bgp_he_net || 1)) * 100) : null, ripe_stat: prefixCrossCheck.ripe_stat, bgp_he_net: prefixCrossCheck.bgp_he_net, note: prefixCrossCheck.note },
|
|
neighbours: { sources: neighbourCrossCheck.bgp_he_net_total != null ? 2 : 1, agreement_pct: neighbourCrossCheck.bgp_he_net_total != null && neighbours.length > 0 ? Math.round((Math.min(neighbours.length, neighbourCrossCheck.bgp_he_net_total) / Math.max(neighbours.length, neighbourCrossCheck.bgp_he_net_total)) * 100) : null, ripe_stat_total: neighbourCrossCheck.ripe_stat_total, bgp_he_net_total: neighbourCrossCheck.bgp_he_net_total },
|
|
},
|
|
overall_confidence: overallConfidence,
|
|
overall_agreement_pct: avgAgreement,
|
|
};
|
|
|
|
return { rpkiCrossCheck, prefixCrossCheck, neighbourCrossCheck, dataQuality };
|
|
}
|
|
|
|
module.exports = { computeCrossChecks };
|