Source code for swh.vulns.naive_server

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

import concurrent.futures
import functools
import json
from pathlib import Path
import sys
from typing import Callable, Dict, Iterator, List, TypeVar

import grpc

from swh.model.exceptions import ValidationError
from swh.model.swhids import CoreSWHID

from .grpc.swhvulns_pb2 import AffectingVulnerabilitiesRequest, DetectedVulnerability
from .grpc.swhvulns_pb2_grpc import (
    VulnerabilityServiceServicer,
    add_VulnerabilityServiceServicer_to_server,
)

F = TypeVar("F", bound=Callable)


[docs] def log_errors(f: F) -> F: @functools.wraps(f) def newf(*args, **kwargs): try: return f(*args, **kwargs) except KeyboardInterrupt: raise except Exception: import traceback traceback.print_exc() raise return newf # type: ignore[return-value]
[docs] class NaiveVulnerabilityServicer(VulnerabilityServiceServicer): """The simplest implementation of :class:`VulnerabilityServiceServicer`, which just returns :class:`DetectedVulnerability` from an in-memory list.""" vulnerabilities: Dict[CoreSWHID, List[DetectedVulnerability]] = {} def __init__(self, vulnerabilities: List[DetectedVulnerability]): self.vulnerabilities = {} for vuln in vulnerabilities: self.vulnerabilities.setdefault( CoreSWHID.from_string(vuln.swhid), [] ).append(vuln)
[docs] @log_errors def AffectingVulnerabilities( self, request: AffectingVulnerabilitiesRequest, context ) -> Iterator[DetectedVulnerability]: swhids = [] for string_swhid in request.swhid: try: swhids.append(CoreSWHID.from_string(string_swhid)) except ValidationError: context.set_code(grpc.StatusCode.INVALID_ARGUMENT) context.set_details(f"Invalid SWHID: {string_swhid}") return for swhid in swhids: yield from self.vulnerabilities.get(swhid) or []
[docs] def run(address: str, database: List[DetectedVulnerability]) -> None: """ Starts :class:`NaiveVulnerabilityServicer` and never returns. ``address`` should be ``<hostname>:<port>``, eg. ``[::]:12345`` """ servicer = NaiveVulnerabilityServicer(database) server = grpc.server(concurrent.futures.ThreadPoolExecutor(max_workers=10)) add_VulnerabilityServiceServicer_to_server(servicer, server) server.add_insecure_port(address) server.start() print(f"Server started, listening on {address}") server.wait_for_termination()
[docs] def main(): try: _, address, database_path = sys.argv except ValueError: print(f"Syntax: {sys.argv[0]} [::]:<port> database.json", file=sys.stderr) print() print( "Serves a read-only list of DetectedVulnerability (see :file:`proto/swhvulns.proto`) from a JSON file" ) exit(1) try: database = json.loads(Path(database_path).read_text()) except FileNotFoundError: print(f"{database_path} does not exist", file=sys.stderr) exit(2) try: run(address, database) except KeyboardInterrupt: pass
if __name__ == "__main__": main()