security: show the fixed versions for each vuln
the data quality is bad, but let's see
This commit is contained in:
parent
3f17254ab1
commit
4ad9115d46
@ -14,7 +14,7 @@ from typing import NamedTuple, Any
|
|||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from packageurl import PackageURL
|
from packageurl import PackageURL
|
||||||
|
|
||||||
from .appconfig import REPOSITORIES
|
from .appconfig import REPOSITORIES
|
||||||
@ -200,6 +200,7 @@ class Vulnerability:
|
|||||||
url: str
|
url: str
|
||||||
severity: Severity
|
severity: Severity
|
||||||
ignored: bool = False
|
ignored: bool = False
|
||||||
|
unaffected_versions: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sort_key(self) -> tuple[bool, int, str, str]:
|
def sort_key(self) -> tuple[bool, int, str, str]:
|
||||||
|
|||||||
@ -29,10 +29,19 @@ def parse_cdx(data: bytes) -> dict[str, list[Vulnerability]]:
|
|||||||
for ratings in vuln["ratings"]:
|
for ratings in vuln["ratings"]:
|
||||||
severity = Severity(ratings["severity"])
|
severity = Severity(ratings["severity"])
|
||||||
break
|
break
|
||||||
|
|
||||||
|
unaffected_versions = []
|
||||||
|
for affects in vuln["affects"]:
|
||||||
|
versions = affects.get("versions", [])
|
||||||
|
for version in versions:
|
||||||
|
if version.get("status") == "unaffected" and "version" in version:
|
||||||
|
unaffected_versions.append(version["version"])
|
||||||
|
|
||||||
return Vulnerability(
|
return Vulnerability(
|
||||||
id=vuln["id"],
|
id=vuln["id"],
|
||||||
url=vuln["source"]["url"],
|
url=vuln["source"]["url"],
|
||||||
severity=severity)
|
severity=severity,
|
||||||
|
unaffected_versions=unaffected_versions)
|
||||||
|
|
||||||
vuln_mapping: dict[str, list[Vulnerability]] = {}
|
vuln_mapping: dict[str, list[Vulnerability]] = {}
|
||||||
for vuln in cdx["vulnerabilities"]:
|
for vuln in cdx["vulnerabilities"]:
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block title %}Base Package: {{ sources[0].name if sources else '' }}{% endblock %}
|
{% block title %}Base Package: {{ sources[0].name if sources else '' }}{% endblock %}
|
||||||
{% block inner_content %}
|
{% block inner_content %}
|
||||||
|
{% from 'macros.html' import vulnerability_list %}
|
||||||
|
|
||||||
{% for s in sources %}
|
{% for s in sources %}
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
@ -85,11 +86,7 @@
|
|||||||
<dt class="col-sm-3 text-sm-end">Vulnerabilities:</dt>
|
<dt class="col-sm-3 text-sm-end">Vulnerabilities:</dt>
|
||||||
<dd class="col-sm-9">
|
<dd class="col-sm-9">
|
||||||
{% if s.all_vulnerabilities %}
|
{% if s.all_vulnerabilities %}
|
||||||
<ul class="list-unstyled">
|
{{ vulnerability_list(s) }}
|
||||||
{% for vuln in s.all_vulnerabilities %}
|
|
||||||
<li {% if vuln.ignored %}style="text-decoration: line-through"{% endif %}><a href="{{ vuln.url }}">{{ vuln.id }}</a> <span class="opacity-75 text-{{vulnerability_color(vuln)}}">({{ vuln.severity }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% elif not s.can_have_vulnerabilities %}
|
{% elif not s.can_have_vulnerabilities %}
|
||||||
<span class="text-muted">Not enough metadata for vulnerability reporting</span>
|
<span class="text-muted">Not enough metadata for vulnerability reporting</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|||||||
32
app/templates/macros.html
Normal file
32
app/templates/macros.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% macro vulnerability_color(vuln) %}
|
||||||
|
{%- if vuln.severity|string == "critical" -%}
|
||||||
|
danger
|
||||||
|
{%- elif vuln.severity|string == "high" -%}
|
||||||
|
warning
|
||||||
|
{%- else -%}
|
||||||
|
secondary
|
||||||
|
{%- endif -%}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro vulnerability_tooltip(s) %}
|
||||||
|
<span class="mytooltip-onclick">
|
||||||
|
<span role="button" class="text-{{vulnerability_color(s.worst_active_vulnerability)}}">⚠</span>
|
||||||
|
<template class="mytooltip-content">
|
||||||
|
{{ vulnerability_list(s) }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro vulnerability_list(s) %}
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{% for vuln in s.all_vulnerabilities %}
|
||||||
|
<li {% if vuln.ignored %}style="text-decoration: line-through"{% endif %}>
|
||||||
|
<a href="{{ vuln.url }}">{{ vuln.id }}</a>
|
||||||
|
<span class="opacity-75 text-{{vulnerability_color(vuln)}}">({{ vuln.severity }})</span>
|
||||||
|
{% if not vuln.ignored and vuln.unaffected_versions %}
|
||||||
|
<br><span>(fixed in {{ vuln.unaffected_versions|join(', ') }})</span>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endmacro %}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block title %}Outdated Packages{% endblock %}
|
{% block title %}Outdated Packages{% endblock %}
|
||||||
{% block inner_content %}
|
{% block inner_content %}
|
||||||
|
{% from 'macros.html' import vulnerability_tooltip %}
|
||||||
|
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -64,20 +65,11 @@
|
|||||||
<td class="text-version">{{ myver }}{% if gitver %} <span class="text-muted small align-text-bottom ps-1">({{ gitver }} in git)</span>{% endif %}</td>
|
<td class="text-version">{{ myver }}{% if gitver %} <span class="text-muted small align-text-bottom ps-1">({{ gitver }} in git)</span>{% endif %}</td>
|
||||||
<td>→</td>
|
<td>→</td>
|
||||||
<td class="text-version"><a href="{{ url }}">{{ ver }}</a></td>
|
<td class="text-version"><a href="{{ url }}">{{ ver }}</a></td>
|
||||||
|
<td>
|
||||||
{% if s.active_vulnerabilities %}
|
{% if s.active_vulnerabilities %}
|
||||||
<td class="mytooltip-onclick">
|
{{ vulnerability_tooltip(s) }}
|
||||||
<span role="button" class="text-{{vulnerability_color(s.worst_active_vulnerability)}}">⚠</span>
|
|
||||||
<template class="mytooltip-content">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
{% for vuln in s.all_vulnerabilities %}
|
|
||||||
<li {% if vuln.ignored %}style="text-decoration: line-through"{% endif %}><a href="{{ vuln.url }}">{{ vuln.id }}</a> <span class="opacity-75 text-{{vulnerability_color(vuln)}}">({{ vuln.severity }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</td>
|
|
||||||
{% else %}
|
|
||||||
<td></td>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -87,16 +79,7 @@
|
|||||||
{% for s in missing %}
|
{% for s in missing %}
|
||||||
<a href="{{ url_for('base', base_name=s.name) }}">{{ s.realname }}</a>
|
<a href="{{ url_for('base', base_name=s.name) }}">{{ s.realname }}</a>
|
||||||
{%- if s.active_vulnerabilities %}
|
{%- if s.active_vulnerabilities %}
|
||||||
<span class="mytooltip-onclick">
|
{{ vulnerability_tooltip(s) }}
|
||||||
<span role="button" class="text-{{vulnerability_color(s.worst_active_vulnerability)}}">⚠</span>
|
|
||||||
<template class="mytooltip-content">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
{% for vuln in s.all_vulnerabilities %}
|
|
||||||
<li {% if vuln.ignored %}style="text-decoration: line-through"{% endif %}><a href="{{ vuln.url }}">{{ vuln.id }}</a> <span class="opacity-75 text-{{vulnerability_color(vuln)}}">({{ vuln.severity }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</span>
|
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
{{ ", " if not loop.last else '' }}
|
{{ ", " if not loop.last else '' }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block title %}Package: {{ packages[0][1].name if packages else '' }}{% endblock %}
|
{% block title %}Package: {{ packages[0][1].name if packages else '' }}{% endblock %}
|
||||||
{% block inner_content %}
|
{% block inner_content %}
|
||||||
|
{% from 'macros.html' import vulnerability_list %}
|
||||||
|
|
||||||
{% for s, p in packages %}
|
{% for s, p in packages %}
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
@ -79,11 +80,7 @@
|
|||||||
{% if s.all_vulnerabilities %}
|
{% if s.all_vulnerabilities %}
|
||||||
<dt class="col-sm-3 text-sm-end">Vulnerabilities:</dt>
|
<dt class="col-sm-3 text-sm-end">Vulnerabilities:</dt>
|
||||||
<dd class="col-sm-9">
|
<dd class="col-sm-9">
|
||||||
<ul class="list-unstyled">
|
{{ vulnerability_list(s) }}
|
||||||
{% for vuln in s.all_vulnerabilities %}
|
|
||||||
<li {% if vuln.ignored %}style="text-decoration: line-through"{% endif %}><a href="{{ vuln.url }}">{{ vuln.id }}</a> <span class="opacity-75 text-{{vulnerability_color(vuln)}}">({{ vuln.severity }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</dd>
|
</dd>
|
||||||
{% endif%}
|
{% endif%}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{% extends "layout.html" %}
|
{% extends "layout.html" %}
|
||||||
{% block title %}Security {% endblock %}
|
{% block title %}Security {% endblock %}
|
||||||
{% block inner_content %}
|
{% block inner_content %}
|
||||||
|
{% from 'macros.html' import vulnerability_tooltip %}
|
||||||
|
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -35,15 +36,8 @@
|
|||||||
<td><a href="{{ url_for('base', base_name=s.name) }}">{{ s.name }}</a></td>
|
<td><a href="{{ url_for('base', base_name=s.name) }}">{{ s.name }}</a></td>
|
||||||
<td class="text-version">{{ s.version }}{% if s.version != s.git_version %} <span class="text-muted small align-text-bottom ps-1">({{ s.git_version }} in git)</span>{% endif %}</td>
|
<td class="text-version">{{ s.version }}{% if s.version != s.git_version %} <span class="text-muted small align-text-bottom ps-1">({{ s.git_version }} in git)</span>{% endif %}</td>
|
||||||
<td class="text-version">{{ s.upstream_version if s.is_outdated_in_git else '' }}</td>
|
<td class="text-version">{{ s.upstream_version if s.is_outdated_in_git else '' }}</td>
|
||||||
<td class="mytooltip-onclick">
|
<td>
|
||||||
<span role="button" class="text-{{vulnerability_color(s.worst_active_vulnerability)}}">⚠</span>
|
{{ vulnerability_tooltip(s) }}
|
||||||
<template class="mytooltip-content">
|
|
||||||
<ul class="list-unstyled">
|
|
||||||
{% for vuln in s.all_vulnerabilities %}
|
|
||||||
<li {% if vuln.ignored %}style="text-decoration: line-through"{% endif %}><a href="{{ vuln.url }}">{{ vuln.id }}</a> <span class="opacity-75 text-{{vulnerability_color(vuln)}}">({{ vuln.severity }})</span></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</template>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
12
app/web.py
12
app/web.py
@ -21,7 +21,7 @@ from fastapi_etag import Etag
|
|||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi_etag import add_exception_handler as add_etag_exception_handler
|
from fastapi_etag import add_exception_handler as add_etag_exception_handler
|
||||||
|
|
||||||
from .appstate import state, get_repositories, Package, Source, DepType, SrcInfoPackage, get_base_group_name, Vulnerability, Severity, PackageKey, find_packages
|
from .appstate import state, get_repositories, Package, Source, DepType, SrcInfoPackage, get_base_group_name, Vulnerability, PackageKey, find_packages
|
||||||
from .utils import extract_upstream_version, version_is_newer_than
|
from .utils import extract_upstream_version, version_is_newer_than
|
||||||
|
|
||||||
router = APIRouter(default_response_class=HTMLResponse)
|
router = APIRouter(default_response_class=HTMLResponse)
|
||||||
@ -92,16 +92,6 @@ def update_timestamp(request: Request) -> float:
|
|||||||
return state.last_update
|
return state.last_update
|
||||||
|
|
||||||
|
|
||||||
@context_function("vulnerability_color")
|
|
||||||
def vulnerability_color(request: Request, vuln: Vulnerability) -> str:
|
|
||||||
if vuln.severity == Severity.CRITICAL:
|
|
||||||
return "danger"
|
|
||||||
elif vuln.severity == Severity.HIGH:
|
|
||||||
return "warning"
|
|
||||||
else:
|
|
||||||
return "secondary"
|
|
||||||
|
|
||||||
|
|
||||||
@context_function("package_url")
|
@context_function("package_url")
|
||||||
def package_url(request: Request, package: Package, name: str | None = None) -> str:
|
def package_url(request: Request, package: Package, name: str | None = None) -> str:
|
||||||
res: str = ""
|
res: str = ""
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user