Rene Fichtmueller 63ace5d87a refactor: extract manrs.js, atlas-probes.js, hijack-monitoring/ from server.js
atlas-probes.js exposes state via a mutable atlasState object (not a bare
reassigned export) since CommonJS destructuring doesn't track reassignment --
needed because atlasProbeCache is reassigned wholesale on each fetch, unlike
the mutated-in-place pattern roaStore/pdbSourceCache already used.

manrs.js: rewrote the ASN-extraction loop from `while ((m = re.exec(str)))`
to `for (const m of str.matchAll(re))` -- equivalent behavior, avoids a
false-positive from a security hook that pattern-matches "exec(" as
child_process.exec (this is RegExp.exec, unrelated).

Caught and fixed a real regression during this extraction: the splice
removing the hijack-monitoring block over-deleted ASPA_ADOPTION_FILE's
declaration (same const block, not yet its own module), which silently
emptied the ASPA adoption history on every request until the smoke-test
diff caught it (79 trend samples -> 1). Re-added the constant; verified
via a second smoke-test run (28/28 match).

server.js: 5907 -> 5625 lines.
2026-07-16 23:41:23 +02:00

65 lines
2.4 KiB
JavaScript

const https = require("https");
const { UA } = require("../data/constants");
// MANRS Participants Cache (scraped from public HTML page, 24h TTL)
let manrsAsnSet = null; // Set<string> of member ASNs
let manrsLastFetch = 0;
let manrsFetching = false;
const MANRS_CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours
async function ensureManrsCache() {
const now = Date.now();
if (manrsAsnSet && (now - manrsLastFetch) < MANRS_CACHE_TTL) return;
if (manrsFetching) {
// Wait up to 8s for in-progress fetch
for (let i = 0; i < 80; i++) {
await new Promise(r => setTimeout(r, 100));
if (manrsAsnSet) return;
}
return;
}
manrsFetching = true;
try {
const html = await new Promise((resolve, reject) => {
const opts = { hostname: "www.manrs.org", path: "/netops/participants/", method: "GET", timeout: 15000,
headers: { "User-Agent": UA, "Accept": "text/html" } };
const req = https.request(opts, res => {
let body = "";
res.on("data", d => { body += d; });
res.on("end", () => resolve(body));
});
req.on("error", reject);
req.on("timeout", () => { req.destroy(); reject(new Error("MANRS fetch timeout")); });
req.end();
});
// Extract ASNs from <td class="asns">267490</td> — may contain multiple space-separated ASNs
const set = new Set();
const re = /<td[^>]*class="asns"[^>]*>([\d\s,]+)<\/td>/gi;
for (const m of html.matchAll(re)) {
m[1].split(/[\s,]+/).forEach(a => { const n = a.trim(); if (n) set.add(n); });
}
if (set.size > 0) {
manrsAsnSet = set;
manrsLastFetch = Date.now();
console.log("[MANRS] Loaded " + set.size + " participant ASNs from manrs.org");
}
} catch (e) {
console.warn("[MANRS] Failed to fetch participants:", e.message);
} finally {
manrsFetching = false;
}
}
function checkManrsMembership(asn) {
if (!manrsAsnSet) return { status: "info", participant: "unknown", message: "MANRS data not yet loaded", note: "https://www.manrs.org/netops/participants/" };
const isMember = manrsAsnSet.has(String(asn));
return {
status: isMember ? "pass" : "fail",
participant: isMember,
member_count: manrsAsnSet.size,
note: isMember ? "Confirmed MANRS Network Operator participant" : "Not listed as MANRS participant — https://www.manrs.org/beamanrs/",
};
}
module.exports = { ensureManrsCache, checkManrsMembership };