fix: local Postgres RPKI validation query was broken (double CIDR suffix + wrong containment operator)
All checks were successful
build-verify / build-verify (push) Successful in 15s

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.
This commit is contained in:
Rene Fichtmueller 2026-07-17 08:42:56 +02:00
parent 381db11a5c
commit 4c286d7f2d

View File

@ -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]
);