Source code for swh.vulns.storage.postgresql

# Copyright (C) 2026  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information

from datetime import datetime
from typing import Any, Dict, List, Optional

import psycopg
from psycopg.rows import dict_row
from psycopg.types.json import Jsonb
import psycopg_pool

from swh.core.db import BaseDb
from swh.core.db.common import db_transaction
from swh.model.swhids import CoreSWHID
from swh.vulns.osv.model import OSVVulnerabilityEvent
from swh.vulns.storage.interface import VulnerabilitiesStorageInterface


[docs] class VulnerabilitiesStorage(VulnerabilitiesStorageInterface): current_version = 1 def __init__( self, db, min_pool_conns: int = 1, max_pool_conns: int = 10, ): """ Args: db: either a libpq connection string, or a psycopg connection """ self._db = None if isinstance(db, psycopg.Connection): self._pool = None self._db = BaseDb(db) else: self._pool = psycopg_pool.ConnectionPool( conninfo=db, min_size=min_pool_conns, max_size=max_pool_conns, kwargs={"row_factory": dict_row}, ) # Wait for the first connection to be ready, and raise the # appropriate exception if connection fails self._pool.open(wait=True, timeout=1)
[docs] def get_db(self): if self._db: return self._db assert self._pool db = BaseDb.from_pool(self._pool) return db
[docs] def put_db(self, db): if db is not self._db: db.put_conn()
[docs] @db_transaction() def osv_vulnerability_report_add( self, vulnerability_id: str, processing_date: datetime, json_report: Dict[str, Any], db, cur, ): report = Jsonb(json_report) cur.execute( """ INSERT INTO osv_report (vulnerability_id, last_processed, json_report) VALUES (%s, %s, %s) ON CONFLICT(vulnerability_id) DO UPDATE SET last_processed = %s, json_report = %s """, (vulnerability_id, processing_date, report, processing_date, report), )
[docs] @db_transaction() def osv_vulnerability_report_last_processing_date( self, vulnerability_id: str, db, cur, ) -> Optional[datetime]: row = cur.execute( """ SELECT last_processed from osv_report WHERE vulnerability_id = %s """, (vulnerability_id,), ).fetchone() return row["last_processed"] if row else None
[docs] @db_transaction() def osv_vulnerability_report_get( self, vulnerability_id: str, db, cur, ) -> Optional[Dict[str, Any]]: row = cur.execute( """ SELECT json_report from osv_report WHERE vulnerability_id = %s """, (vulnerability_id,), ).fetchone() return row["json_report"] if row else None
vulnerability_event_cols = [ "vulnerability_id", "vulnerability_severity", "event_type", "origin_url", "swhid", "version", ]
[docs] @db_transaction() def osv_vulnerability_event_add( self, vulnerabilities_events: List[OSVVulnerabilityEvent], db=None, cur=None ): for vulnerability_event in vulnerabilities_events: vulnerability_event_d = vulnerability_event.to_dict() cur.execute( "INSERT INTO osv_vulnerability_event " f"({', '.join(self.vulnerability_event_cols)}) " "VALUES (%s, %s, %s, %s, %s, %s) ON CONFLICT DO NOTHING", tuple( vulnerability_event_d[key] for key in self.vulnerability_event_cols ), )
[docs] @db_transaction() def osv_vulnerability_event_get_by_id( self, vulnerabilities_ids: List[str], db=None, cur=None ) -> List[OSVVulnerabilityEvent]: query = ( f"SELECT {', '.join(self.vulnerability_event_cols)} " "FROM osv_vulnerability_event " "WHERE vulnerability_id = ANY(%s)" ) cur.execute(query, (vulnerabilities_ids,)) return [OSVVulnerabilityEvent.from_dict(row) for row in cur]
[docs] @db_transaction() def osv_vulnerability_event_get_by_swhid( self, swhids: List[CoreSWHID], db=None, cur=None ) -> List[OSVVulnerabilityEvent]: query = ( f"SELECT {', '.join(self.vulnerability_event_cols)} " "FROM osv_vulnerability_event " "WHERE swhid = ANY(%s)" ) cur.execute(query, ([str(swhid) for swhid in swhids],)) return [OSVVulnerabilityEvent.from_dict(row) for row in cur]
[docs] @db_transaction() def osv_vulnerability_event_get_by_origin_url( self, origin_urls: List[str], db=None, cur=None ) -> List[OSVVulnerabilityEvent]: query = ( f"SELECT {', '.join(self.vulnerability_event_cols)} " "FROM osv_vulnerability_event " "WHERE origin_url = ANY(%s)" ) cur.execute(query, (origin_urls,)) return [OSVVulnerabilityEvent.from_dict(row) for row in cur]