#!/usr/bin/env python3 """Conservative SQLite index from CVEProject, NVD and OSV raw JSON. Do not mistake this index for a complete vulnerability matching engine. """ import argparse import hashlib import json import os import re import shutil import sqlite3 import subprocess import tempfile import sys from datetime import datetime, timezone from pathlib import Path # Make the top-level importers package importable when invoked as scripts/build.py. sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from importers import IMPORTERS CVE = re.compile(r'^CVE-\d{4}-\d{4,}$', re.I) FILES = ('vulnerabilities','aliases','affected_packages','cpe_matches') def canonical(obj): return json.dumps(obj, ensure_ascii=False, sort_keys=True, separators=(',', ':')) def get_git_revision(path): return subprocess.check_output(['git', '-C', str(path), 'rev-parse', 'HEAD'], text=True).strip() def input_files(repo): d = repo / 'data' if not d.exists(): return # Nur tatsächliche CVE-Datensätze einlesen if repo.name == 'vuln-cve': yield from sorted(d.rglob('CVE-*.json')) return # Andere Quellen unverändert verarbeiten yield from sorted(d.rglob('*.json')) def stream_source(db, name, path): importer = IMPORTERS[name] count = 0 bad = 0 scanned = 0 for file in input_files(path): scanned += 1 try: with file.open('r', encoding='utf-8') as stream: obj = json.load(stream) count += int(bool(importer(db, obj))) except (ValueError, UnicodeError, TypeError, KeyError, sqlite3.Error) as exc: bad += 1 if bad <= 5: print(f'INVALID {name} {file}: {exc}', flush=True) if bad: raise RuntimeError(f'{name}: {bad} invalid records; database not published') if name == 'vuln-nvd' and count == 0: raise RuntimeError('vuln-nvd: no usable documents; check source data and JSON shape') if name in ('vuln-cve', 'vuln-osv') and count == 0: raise RuntimeError(f'{name}: no usable documents; refusing to publish') print(f'{name}: scanned={scanned} accepted={count}', flush=True) return count def dump_table(db,table): # memory efficient cursor; a canonical row object for deltas cols=[r[1] for r in db.execute(f'PRAGMA table_info({table})')] for row in db.execute(f'SELECT * FROM {table} ORDER BY 1'): yield row[0],dict(zip(cols,row)) def make_delta(old_path,new_db,path): old=sqlite3.connect(old_path) with path.open('w',encoding='utf-8') as f: for table in FILES: prev=dump_table(old,table); curr=dump_table(new_db,table) a=next(prev,None); b=next(curr,None) while a is not None or b is not None: if a is not None and (b is None or a[0]