Files
rengine/api/app/services/asset_query/errors.py
T
Yogesh OjhaandClaude Opus 5 3b97ece7e0 feat(vulns): corroborate findings by shared CVE or CWE at the same location
A finding is corroborated when another check fired at the same matched_at,
names the same CVE or CWE, and neither is informational or suppressed. The
peer carries its basis, the shared identifiers and its scanner, so a second
scanner confirming the same weakness is distinguishable from a second nuclei
template. Co-location alone no longer counts: four unrelated TLS defects on
one endpoint used to vouch for each other.

Surface it the way the grammar surfaces everything else: an is:corroborated
flag, a Confirmed quick filter, two counted example leads, a Location group
dimension, and a tiebreaker in the risk sort after severity and KEV. The row
carries a confirmed badge beside new and KEV instead of trailing text on the
origin line; the detail sheet states each peer's basis and links to the other
findings at that location.

Add a btree over (scan_id, matched_at) - the equality lookup was probing the
trigram GIN index at 8.2ms a key - and disable JIT on the vulnerability query
paths, where the set-returning expansion's 100-rows-a-row estimate bought
267ms of compilation per request. On 60k findings: search 510ms to 240ms,
issues 1380ms to 440ms (830ms before the feature existed).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZKQQx39KeiDo8QE9XXzf3
2026-09-04 20:37:02 +05:30

34 lines
1.1 KiB
Python

from __future__ import annotations
from sqlalchemy.exc import DBAPIError
from shared.models.asset_query import QueryError
STATEMENT_TIMEOUT = "SET LOCAL statement_timeout = '20s'"
# a set-returning expansion is estimated at 100 rows a row, and the bogus cost buys 270ms of JIT
NO_JIT = "SET LOCAL jit = off"
QUERY_SQLSTATES = {
"2201B": (
"That regular expression is not valid here.",
"PostgreSQL regular expressions differ slightly from PCRE.",
),
"2201G": ("That regular expression is not valid here.", None),
"57014": (
"That search took too long to run.",
"Add a field filter to narrow it down.",
),
"22003": ("A number in that query is out of range.", None),
"22P02": ("A value in that query has the wrong shape.", None),
"54000": ("That search term is too large to index.", None),
}
def query_error_for(exc: DBAPIError) -> QueryError | None:
sqlstate = getattr(exc.orig, "sqlstate", None) or getattr(exc.orig, "pgcode", None)
known = QUERY_SQLSTATES.get(str(sqlstate))
if known is None:
return None
message, hint = known
return QueryError(message=message, hint=hint)