Compare commits
4 Commits
5d421dcd28
...
b06aedfc3b
| Author | SHA1 | Date | |
|---|---|---|---|
| b06aedfc3b | |||
| 392c8f4fe5 | |||
| 3c00f05263 | |||
| c57ef61adb |
BIN
agents/QualysCloudAgent_7.3.0.40.deb
Normal file
BIN
agents/QualysCloudAgent_7.3.0.40.deb
Normal file
Binary file not shown.
BIN
agents/QualysCloudAgent_7.3.0.40.rpm
Normal file
BIN
agents/QualysCloudAgent_7.3.0.40.rpm
Normal file
Binary file not shown.
BIN
agents/QualysCloudAgent_LINUX_7.1.0.37.rpm
Normal file
BIN
agents/QualysCloudAgent_LINUX_7.1.0.37.rpm
Normal file
Binary file not shown.
BIN
agents/QualysCloudAgent_LINUX_UBUNTU_7.2.0.38.deb
Normal file
BIN
agents/QualysCloudAgent_LINUX_UBUNTU_7.2.0.38.deb
Normal file
Binary file not shown.
BIN
agents/QualysCloudAgent_LINUX_UBUNTU_7.2.3.8.deb
Normal file
BIN
agents/QualysCloudAgent_LINUX_UBUNTU_7.2.3.8.deb
Normal file
Binary file not shown.
@ -305,7 +305,7 @@ async def qualys_resync_assets(request: Request, db=Depends(get_db)):
|
|||||||
if result.get("ok") and result.get("assets"):
|
if result.get("ok") and result.get("assets"):
|
||||||
_save_api_results_to_db(db, result["assets"])
|
_save_api_results_to_db(db, result["assets"])
|
||||||
ok += 1
|
ok += 1
|
||||||
return RedirectResponse(url=_bulk_return_url(form, f"resync_{ok}"), status_code=303)
|
return RedirectResponse(url=_bulk_return_url(form, f"resync_{ok}") + "&force=1", status_code=303)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/qualys/bulk/tags-for-assets")
|
@router.get("/qualys/bulk/tags-for-assets")
|
||||||
@ -466,11 +466,28 @@ async def qualys_search(request: Request, db=Depends(get_db),
|
|||||||
|
|
||||||
all_tags = db.execute(text("SELECT qualys_tag_id, name FROM qualys_tags ORDER BY name")).fetchall()
|
all_tags = db.execute(text("SELECT qualys_tag_id, name FROM qualys_tags ORDER BY name")).fetchall()
|
||||||
|
|
||||||
|
# KPI : total / avec vuln / sans vuln + filtrage vuln_filter (with|zero)
|
||||||
|
vuln_filter = request.query_params.get("vuln_filter", "")
|
||||||
|
def _vuln_total(a):
|
||||||
|
vc = vuln_map.get(str(a.ip_address), {})
|
||||||
|
if isinstance(vc, dict):
|
||||||
|
return int(vc.get("total", 0) or 0)
|
||||||
|
return int(vc or 0)
|
||||||
|
kpi_total = len(assets) if assets else 0
|
||||||
|
kpi_with_vuln = sum(1 for a in assets if _vuln_total(a) > 0) if assets else 0
|
||||||
|
kpi_zero_vuln = kpi_total - kpi_with_vuln
|
||||||
|
if assets and vuln_filter == "with":
|
||||||
|
assets = [a for a in assets if _vuln_total(a) > 0]
|
||||||
|
elif assets and vuln_filter == "zero":
|
||||||
|
assets = [a for a in assets if _vuln_total(a) == 0]
|
||||||
|
|
||||||
ctx = base_context(request, db, user)
|
ctx = base_context(request, db, user)
|
||||||
ctx.update({
|
ctx.update({
|
||||||
"app_name": APP_NAME, "assets": assets, "search": search,
|
"app_name": APP_NAME, "assets": assets, "search": search,
|
||||||
"field": field, "api_msg": api_msg,
|
"field": field, "api_msg": api_msg,
|
||||||
"all_tags": all_tags, "vuln_map": vuln_map,
|
"all_tags": all_tags, "vuln_map": vuln_map,
|
||||||
|
"kpi_total": kpi_total, "kpi_with_vuln": kpi_with_vuln, "kpi_zero_vuln": kpi_zero_vuln,
|
||||||
|
"vuln_filter": vuln_filter,
|
||||||
"cache_info": cache_info,
|
"cache_info": cache_info,
|
||||||
"can_edit_qualys": can_edit(perms, "qualys"),
|
"can_edit_qualys": can_edit(perms, "qualys"),
|
||||||
"msg": request.query_params.get("msg"),
|
"msg": request.query_params.get("msg"),
|
||||||
@ -499,7 +516,7 @@ def qualys_agents_page(request: Request, db=Depends(get_db)):
|
|||||||
# Serveurs sans agent Qualys (match via server_id pour gerer alias/IP)
|
# Serveurs sans agent Qualys (match via server_id pour gerer alias/IP)
|
||||||
# Exclut les workstations Win10/11 = portables/postes dev
|
# Exclut les workstations Win10/11 = portables/postes dev
|
||||||
no_agent_rows = db.execute(text("""
|
no_agent_rows = db.execute(text("""
|
||||||
SELECT s.hostname, s.os_family, s.etat, d.name as domain, e.name as env, z.name as zone
|
SELECT s.hostname, s.os_family, s.os_version, s.etat, d.name as domain, e.name as env, z.name as zone
|
||||||
FROM servers s
|
FROM servers s
|
||||||
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
||||||
LEFT JOIN domains d ON de.domain_id = d.id
|
LEFT JOIN domains d ON de.domain_id = d.id
|
||||||
@ -516,7 +533,7 @@ def qualys_agents_page(request: Request, db=Depends(get_db)):
|
|||||||
AND COALESCE(s.os_version, '') NOT ILIKE '%Workstation%'
|
AND COALESCE(s.os_version, '') NOT ILIKE '%Workstation%'
|
||||||
ORDER BY s.hostname
|
ORDER BY s.hostname
|
||||||
""")).fetchall()
|
""")).fetchall()
|
||||||
no_agent = [{"hostname": r.hostname, "os_family": r.os_family, "etat": r.etat,
|
no_agent = [{"hostname": r.hostname, "os_family": r.os_family, "os_version": r.os_version or "", "etat": r.etat,
|
||||||
"domain": r.domain or "", "env": r.env or "", "zone": r.zone or ""} for r in no_agent_rows]
|
"domain": r.domain or "", "env": r.env or "", "zone": r.zone or ""} for r in no_agent_rows]
|
||||||
|
|
||||||
# Agents inactifs
|
# Agents inactifs
|
||||||
@ -591,7 +608,7 @@ async def export_no_agent_csv(request: Request, db=Depends(get_db)):
|
|||||||
return RedirectResponse(url="/qualys/agents")
|
return RedirectResponse(url="/qualys/agents")
|
||||||
import io, csv as _csv
|
import io, csv as _csv
|
||||||
rows = db.execute(text("""
|
rows = db.execute(text("""
|
||||||
SELECT s.hostname, s.os_family, s.etat, d.name as domain, e.name as env, z.name as zone
|
SELECT s.hostname, s.os_family, s.os_version, s.etat, d.name as domain, e.name as env, z.name as zone
|
||||||
FROM servers s
|
FROM servers s
|
||||||
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
LEFT JOIN domain_environments de ON s.domain_env_id = de.id
|
||||||
LEFT JOIN domains d ON de.domain_id = d.id
|
LEFT JOIN domains d ON de.domain_id = d.id
|
||||||
@ -604,7 +621,7 @@ async def export_no_agent_csv(request: Request, db=Depends(get_db)):
|
|||||||
w = _csv.writer(output, delimiter=";")
|
w = _csv.writer(output, delimiter=";")
|
||||||
w.writerow(["Hostname", "OS", "Domaine", "Environnement", "Zone", "Etat"])
|
w.writerow(["Hostname", "OS", "Domaine", "Environnement", "Zone", "Etat"])
|
||||||
for r in rows:
|
for r in rows:
|
||||||
w.writerow([r.hostname, r.os_family or "", r.domain or "", r.env or "", r.zone or "", r.etat or ""])
|
w.writerow([r.hostname, r.os_family or "", r.os_version or "", r.domain or "", r.env or "", r.zone or "", r.etat or ""])
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
iter(["\ufeff" + output.getvalue()]),
|
iter(["\ufeff" + output.getvalue()]),
|
||||||
|
|||||||
@ -215,6 +215,7 @@ function refreshAgents(mode) {
|
|||||||
<thead><tr>
|
<thead><tr>
|
||||||
<th class="text-left p-2">Hostname</th>
|
<th class="text-left p-2">Hostname</th>
|
||||||
<th class="p-2">OS</th>
|
<th class="p-2">OS</th>
|
||||||
|
<th class="p-2">Version OS</th>
|
||||||
<th class="p-2">Domaine</th>
|
<th class="p-2">Domaine</th>
|
||||||
<th class="p-2">Env</th>
|
<th class="p-2">Env</th>
|
||||||
<th class="p-2">Zone</th>
|
<th class="p-2">Zone</th>
|
||||||
@ -231,6 +232,7 @@ function refreshAgents(mode) {
|
|||||||
">
|
">
|
||||||
<td class="p-2 font-mono text-cyber-accent">{{ s.hostname }}</td>
|
<td class="p-2 font-mono text-cyber-accent">{{ s.hostname }}</td>
|
||||||
<td class="p-2 text-center">{{ s.os_family or '-' }}</td>
|
<td class="p-2 text-center">{{ s.os_family or '-' }}</td>
|
||||||
|
<td class="p-2 text-center text-xs text-gray-300">{{ s.os_version or '-' }}</td>
|
||||||
<td class="p-2 text-center text-gray-400">{{ s.domain or '-' }}</td>
|
<td class="p-2 text-center text-gray-400">{{ s.domain or '-' }}</td>
|
||||||
<td class="p-2 text-center">{{ s.env or '-' }}</td>
|
<td class="p-2 text-center">{{ s.env or '-' }}</td>
|
||||||
<td class="p-2 text-center">{% if s.zone == 'DMZ' %}<span class="badge badge-red">DMZ</span>{% else %}{{ s.zone or '-' }}{% endif %}</td>
|
<td class="p-2 text-center">{% if s.zone == 'DMZ' %}<span class="badge badge-red">DMZ</span>{% else %}{{ s.zone or '-' }}{% endif %}</td>
|
||||||
|
|||||||
@ -48,6 +48,26 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if search and kpi_total is defined %}
|
||||||
|
<div class="grid grid-cols-3 gap-3 mb-3">
|
||||||
|
<a href="/qualys/search?field={{ field }}&search={{ search }}" class="card p-3 hover:border-cyber-accent transition {% if not vuln_filter %}border-cyber-accent{% endif %}">
|
||||||
|
<div class="text-xs text-gray-400 uppercase">Total</div>
|
||||||
|
<div class="text-2xl font-bold text-cyber-accent">{{ kpi_total }}</div>
|
||||||
|
<div class="text-xs text-gray-500">{% if vuln_filter %}Voir tous{% else %}Affichage actuel{% endif %}</div>
|
||||||
|
</a>
|
||||||
|
<a href="/qualys/search?field={{ field }}&search={{ search }}&vuln_filter=with" class="card p-3 hover:border-cyber-red transition {% if vuln_filter == 'with' %}border-cyber-red{% endif %}">
|
||||||
|
<div class="text-xs text-gray-400 uppercase">Avec vulnerabilites</div>
|
||||||
|
<div class="text-2xl font-bold text-red-400">{{ kpi_with_vuln }}</div>
|
||||||
|
<div class="text-xs text-gray-500">Cliquer pour filtrer</div>
|
||||||
|
</a>
|
||||||
|
<a href="/qualys/search?field={{ field }}&search={{ search }}&vuln_filter=zero" class="card p-3 hover:border-cyber-green transition {% if vuln_filter == 'zero' %}border-cyber-green{% endif %}">
|
||||||
|
<div class="text-xs text-gray-400 uppercase">Sans vulnerabilite</div>
|
||||||
|
<div class="text-2xl font-bold text-cyber-green">{{ kpi_zero_vuln }}</div>
|
||||||
|
<div class="text-xs text-gray-500">Cliquer pour filtrer</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- Panel détail -->
|
<!-- Panel détail -->
|
||||||
<div id="detail-loading" class="card mb-4 p-6 flex items-center justify-center gap-3" style="display:none;">
|
<div id="detail-loading" class="card mb-4 p-6 flex items-center justify-center gap-3" style="display:none;">
|
||||||
<div style="width:20px;height:20px;border:2px solid #22c55e;border-top-color:transparent;border-radius:50%;animation:spin 0.8s linear infinite;"></div>
|
<div style="width:20px;height:20px;border:2px solid #22c55e;border-top-color:transparent;border-radius:50%;animation:spin 0.8s linear infinite;"></div>
|
||||||
|
|||||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user