from __future__ import annotations import json from pathlib import Path from typing import Any, Dict DEFAULT_CONFIG: Dict[str, Any] = { "progress": {"enabled": True}, "output": { "group_by_service": True, "max_deps_per_ecosystem": 20, "max_dev_deps_per_ecosystem": 10, "sections": { "show_service_separator": True, "show_service_header": True, "show_separator": False, "show_id_status": False, "show_created": False, "show_ports": False, "show_mounts": False, "show_versions": True, "show_language": False, "show_source": True, "show_revision": False, "show_deps_source": True, "show_manifests": True, "manifests_mode": "count", "show_raw_deps_count": False, "show_ecosystem_counts": True, "show_effective_deps_count": True, "show_deps_list": False, "show_dev_deps_list": False, "show_code_files": False, "show_osv_summary": True, "show_osv_top_affected": True, "show_osv_sample_ids": False, "show_errors": True, }, }, "vulns": { "min_severity": "MEDIUM", "include_unknown": True, "top_affected": 8, }, "osv": { "chunk_size": 250, "hydrate_details": True, "max_hydrate_ids": 300, }, } def _deep_merge(dst: Dict[str, Any], src: Dict[str, Any]) -> Dict[str, Any]: for k, v in src.items(): if isinstance(v, dict) and isinstance(dst.get(k), dict): dst[k] = _deep_merge(dict(dst[k]), v) else: dst[k] = v return dst def load_config(filename: str = "cvexplorer_config.json") -> Dict[str, Any]: cfg = json.loads(json.dumps(DEFAULT_CONFIG)) # deep copy path = Path.cwd() / filename if not path.exists(): return cfg try: user_cfg = json.loads(path.read_text(encoding="utf-8")) if isinstance(user_cfg, dict): return _deep_merge(cfg, user_cfg) return cfg except Exception: return cfg