From 4c286d7f2d1d1b386e1b86211cec5ebd61236001 Mon Sep 17 00:00:00 2001 From: Rene Fichtmueller Date: Fri, 17 Jul 2026 08:42:56 +0200 Subject: [PATCH] fix: local Postgres RPKI validation query was broken (double CIDR suffix + wrong containment operator) validateRpki()'s covering-ROA query concatenated "/" + max_length onto `prefix`, which is already a `cidr` column with its own mask -- produced invalid literals like "1.1.1.0/24/24" and threw on every single lookup (observed live: "invalid input syntax for type cidr"). Every RPKI completeness check has been silently degrading to "unavailable" since this landed. Also switched << (strictly contained) to <<= (contained-or-equal) in both this query and the anyRoa fallback -- a route announced at exactly the ROA's own prefix length (the most common real-world case) was never matched by << alone, so even a syntactically-valid version of this query would have under-reported RPKI coverage. Verified against production data on Erik: AS13335/1.1.1.0/24 now correctly resolves to a real covering ROA instead of erroring. --- local-db-client.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/local-db-client.js b/local-db-client.js index c3df0e7..9faf0ff 100644 --- a/local-db-client.js +++ b/local-db-client.js @@ -101,10 +101,17 @@ async function validateRpki(prefix, originAsn) { const prefixLength = parseInt(prefixParts[1]); - // Query for covering ROAs + // Query for covering ROAs. `prefix` is already a `cidr` column (its own + // mask, e.g. "1.1.1.0/24") -- appending "/" + max_length a second time + // produced invalid CIDR literals like "1.1.1.0/24/24" and made this + // query fail for every prefix. max_length is a separate upper bound, + // checked below against prefixLength once we have a covering ROA. + // Uses <<= (contained-by-or-equal), not << (strictly contained) -- a + // route announced at exactly the ROA's own prefix length is the most + // common real-world case and << alone misses it entirely. const result = await pool.query( `SELECT * FROM rpki_roas - WHERE $1::cidr << (prefix || '/' || max_length)::cidr + WHERE $1::cidr <<= prefix AND origin_asn = $2 AND expires > NOW() LIMIT 10`, @@ -113,7 +120,7 @@ async function validateRpki(prefix, originAsn) { if (result.rows.length === 0) { const anyRoa = await pool.query( - `SELECT 1 FROM rpki_roas WHERE $1::cidr << prefix AND expires > NOW() LIMIT 1`, + `SELECT 1 FROM rpki_roas WHERE $1::cidr <<= prefix AND expires > NOW() LIMIT 1`, [prefix] );