diff --git a/app/routers/qualys.py b/app/routers/qualys.py index 1388966..06a5ba9 100644 --- a/app/routers/qualys.py +++ b/app/routers/qualys.py @@ -8,7 +8,7 @@ from ..dependencies import get_db, get_current_user, get_user_perms, can_view, c from ..services.qualys_service import ( sync_server_qualys, search_assets_api, get_all_tags_api, create_tag_api, delete_tag_api, add_tag_to_asset_api, - remove_tag_from_asset_api, resync_all_tags, + remove_tag_from_asset_api, resync_all_tags, get_vuln_counts, ) from ..config import APP_NAME @@ -408,13 +408,23 @@ async def qualys_search(request: Request, db=Depends(get_db), api_msg = f"{len(assets)} résultat(s) — source: {source}" + # Enrichir avec vulnérabilités (severity 3,4,5, Confirmed/Potential, Active) + vuln_map = {} + if assets: + asset_ids = [str(a.qualys_asset_id) for a in assets if a.qualys_asset_id] + if asset_ids: + try: + vuln_map = get_vuln_counts(db, ",".join(asset_ids[:50])) + except Exception: + pass + all_tags = db.execute(text("SELECT qualys_tag_id, name FROM qualys_tags ORDER BY name")).fetchall() ctx = base_context(request, db, user) ctx.update({ "app_name": APP_NAME, "assets": assets, "search": search, "field": field, "api_msg": api_msg, - "all_tags": all_tags, + "all_tags": all_tags, "vuln_map": vuln_map, "can_edit_qualys": can_edit(perms, "qualys"), "msg": request.query_params.get("msg"), }) diff --git a/app/services/qualys_service.py b/app/services/qualys_service.py index f2bd53d..9213f76 100644 --- a/app/services/qualys_service.py +++ b/app/services/qualys_service.py @@ -369,3 +369,69 @@ def _find_asset_by_hostname(qualys_url, qualys_user, qualys_pass, hostname, prox except Exception: pass return None + + +def get_vuln_counts(db, qualys_asset_ids): + """Recupere le nombre de vulnerabilites actives severity 3,4,5 pour un ou plusieurs assets. + qualys_asset_ids: str (un ID ou liste separee par virgules) + Retourne dict {asset_id: {severity3, severity4, severity5, total, confirmed, potential}} + """ + qualys_url, qualys_user, qualys_pass, qualys_proxy = _get_qualys_creds(db) + if not qualys_user or not qualys_asset_ids: + return {} + proxies = {"https": qualys_proxy, "http": qualys_proxy} if qualys_proxy else None + + try: + r = requests.post( + f"{qualys_url}/api/2.0/fo/asset/host/vm/detection/", + data={ + "action": "list", + "ids": str(qualys_asset_ids), + "severities": "3,4,5", + "status": "New,Active,Re-Opened", + "show_results": "0", + "output_format": "XML", + }, + auth=(qualys_user, qualys_pass), + verify=False, timeout=120, proxies=proxies, + headers={"X-Requested-With": "Python"}, + ) + except Exception: + return {} + + if r.status_code != 200: + return {} + + txt = r.text + results = {} + + for host_block in txt.split("")[1:]: + host_block = host_block.split("")[0] + host_id = (parse_xml(host_block, "ID") or [""])[0] + if not host_id: + continue + + counts = {"severity3": 0, "severity4": 0, "severity5": 0, + "total": 0, "confirmed": 0, "potential": 0} + + for det_block in host_block.split("")[1:]: + det_block = det_block.split("")[0] + severity = (parse_xml(det_block, "SEVERITY") or ["0"])[0] + det_type = (parse_xml(det_block, "TYPE") or [""])[0] + + sev = int(severity) if severity.isdigit() else 0 + if sev < 3: + continue + if det_type not in ("Confirmed", "Potential"): + continue + + counts["total"] += 1 + if sev == 3: counts["severity3"] += 1 + elif sev == 4: counts["severity4"] += 1 + elif sev == 5: counts["severity5"] += 1 + if det_type == "Confirmed": counts["confirmed"] += 1 + elif det_type == "Potential": counts["potential"] += 1 + + results[str(host_id)] = counts + + return results diff --git a/app/templates/qualys_search.html b/app/templates/qualys_search.html index 8592370..6b46948 100644 --- a/app/templates/qualys_search.html +++ b/app/templates/qualys_search.html @@ -145,6 +145,7 @@ function updateBulkTag() { IP OS Agent + Vulns Tags Actions @@ -165,6 +166,17 @@ function updateBulkTag() { {% if agent %}{{ agent[:10] }} {% else %}N/A{% endif %} + + {% set vc = vuln_map.get(qid|string, {}) if vuln_map else {} %} + {% if vc and vc.total > 0 %} + + {% if vc.severity5 > 0 %}{{ vc.severity5 }} crit {% endif %} + {% if vc.severity4 > 0 %}{{ vc.severity4 }} high {% endif %} + {% if vc.severity3 > 0 %}+{{ vc.severity3 }} med{% endif %} + + {% elif vc is mapping %}0 + {% else %}-{% endif %} + {{ (tl or '-')[:80] }} {% if qid %}