Skip to content

Commit

Permalink
Enable purl slug for package views #904
Browse files Browse the repository at this point in the history
* Use purl slug and URL route for packages
* Use in forms, urls and templates, including a get_absolute_url()
  method.

* Rename VulnerabiltyForm forms to VulnerabiltySearchForm

* Rename PackageForm forms to PackageSearchForm

* Use new pagination template includes in search results templates. The
  pagination is the same repeated at the top and botton of the search
  results

* Display on 20 search results per page.

Signed-off-by: Philippe Ombredanne <pombredanne@nexb.com>
  • Loading branch information
pombredanne committed Sep 8, 2022
1 parent 6504321 commit 681930a
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 445 deletions.
11 changes: 7 additions & 4 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ Version v30.0.0
- Add new attribute `is_resolved`
- Add namespace filter

- We have provided backward compatibility for `url` and `unresolved_vulnerabilities` for now
- We have provided backward compatibility for `url` and `unresolved_vulnerabilities` for now.

- There is a new experimental cpe/ API endpoint to lookup for vulnerabilities by CPE and
- There is a new experimental `cpe/` API endpoint to lookup for vulnerabilities by CPE and
another aliases/ endpoint to lookup for vulnerabilities by aliases. These two endpoints will be
replaced by query parameters on the main vulnerabilities/ endpoint when stabilized.

Expand All @@ -71,8 +71,11 @@ Version v30.0.0

Other:

- we dropped calver to use a plain semver.
- we adopted vers and the new univers library to handle version ranges.
- We dropped calver to use a plain semver.
- We adopted vers and the new univers library to handle version ranges.
- There is a new stored character field in the Package model to store a purl
as-is which helps in the UI and search (rather thna only having a computed
field).


Version v20.10
Expand Down
15 changes: 2 additions & 13 deletions vulnerabilities/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,8 @@

from django import forms

from vulnerabilities.models import Package


def get_known_package_types():
"""
Return a list of known package types.
"""
pkg_types = [(i.type, i.type) for i in Package.objects.distinct("type").all()]
pkg_types.append((None, "Any type"))
return pkg_types


class PackageForm(forms.Form):
class PackageSearchForm(forms.Form):

search = forms.CharField(
required=True,
Expand All @@ -31,7 +20,7 @@ class PackageForm(forms.Form):
)


class VulnerabilityForm(forms.Form):
class VulnerabilitySearchForm(forms.Form):

search = forms.CharField(
required=True,
Expand Down
35 changes: 10 additions & 25 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
import logging

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator
from django.core.validators import MinValueValidator
from django.db import models
from django.dispatch import receiver
from django.urls import reverse
from packageurl import PackageURL
from packageurl.contrib.django.models import PackageURLMixin
from rest_framework.authtoken.models import Token

Expand Down Expand Up @@ -98,7 +96,7 @@ def get_absolute_url(self):
"""
Return this Vulnerability details URL.
"""
return reverse("vulnerability_view", args=[self.vulnerability_id])
return reverse("vulnerability_details", args=[self.vulnerability_id])


class VulnerabilityReference(models.Model):
Expand Down Expand Up @@ -155,10 +153,6 @@ class Package(PackageURLMixin):
A software package with related vulnerabilities.
"""

vulnerabilities = models.ManyToManyField(
to="Vulnerability", through="PackageRelatedVulnerability"
)

# Remove the `qualifers` and `set_package_url` overrides after
# https://github.com/package-url/packageurl-python/pull/35
# https://github.com/package-url/packageurl-python/pull/67
Expand All @@ -171,6 +165,14 @@ class Package(PackageURLMixin):
null=False,
)

vulnerabilities = models.ManyToManyField(
to="Vulnerability", through="PackageRelatedVulnerability"
)

@property
def purl(self):
return self.package_url

class Meta:
unique_together = (
"type",
Expand Down Expand Up @@ -221,28 +223,11 @@ def is_vulnerable(self):
"""
return self.vulnerable_to.exists()

def set_package_url(self, package_url):
"""
Set each field values to the values of the provided `package_url` string
or PackageURL object. Existing values are overwritten including setting
values to None for provided empty values.
"""
if not isinstance(package_url, PackageURL):
package_url = PackageURL.from_string(package_url)

for field_name, value in package_url.to_dict().items():
model_field = self._meta.get_field(field_name)

if value and len(value) > model_field.max_length:
raise ValidationError(f'Value too long for field "{field_name}".')

setattr(self, field_name, value or None)

def get_absolute_url(self):
"""
Return this Package details URL.
"""
return reverse("package_view", args=[self.package_url])
return reverse("package_details", args=[self.purl])


class PackageRelatedVulnerability(models.Model):
Expand Down
39 changes: 39 additions & 0 deletions vulnerabilities/templates/includes/pagination.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<nav class="pagination is-centered is-small" aria-label="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}&search={{ search }}" class="pagination-previous">Previous</a>
{% else %}
<a class="pagination-previous" disabled>Previous</a>
{% endif %}

{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&search={{ search }}" class="pagination-next">Next</a>
{% else %}
<a class="pagination-next" disabled>Next</a>
{% endif %}

<ul class="pagination-list">
{% if page_obj.number != 1%}
<li>
<a href="?page=1&search={{ search }}" class="pagination-link" aria-label="Goto page 1">1</a>
</li>
{% if page_obj.number > 2 %}
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
{% endif %}
{% endif %}
<li>
<a class="pagination-link is-current" aria-label="Page {{ page_obj.number }}" aria-current="page">{{ page_obj.number }}</a>
</li>
{% if page_obj.number != page_obj.paginator.num_pages %}
{% if page_obj.next_page_number != page_obj.paginator.num_pages %}
<li>
<span class="pagination-ellipsis">&hellip;</span>
</li>
{% endif %}
<li>
<a href="?page={{ page_obj.paginator.num_pages }}&search={{ search }}" class="pagination-link" aria-label="Goto page {{ page_obj.paginator.num_pages }}">{{ page_obj.paginator.num_pages }}</a>
</li>
{% endif %}
</ul>
</nav>
123 changes: 57 additions & 66 deletions vulnerabilities/templates/package_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
{% load widget_tweaks %}

{% block title %}
VulnerableCode Package Details
VulnerableCode Package Details - {{ package.purl }}
{% endblock %}

{% block content %}

{% include "navbar.html" %}

<section class="section pt-0">
{% include "package_search_box.html"%}
{% include "package_search_box.html"%}
</section>

{% if package %}
<section class="section pt-0">
<div class="details-container">
<article class="panel is-info panel-header-only">
<div class="panel-heading py-2 is-size-6">
Package details:
Package details:
</div>
</article>

Expand All @@ -29,14 +29,14 @@
<tr>
<td class="two-col-left">
<span
class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
class="has-tooltip-multiline has-tooltip-black has-tooltip-arrow has-tooltip-text-left"
data-tooltip="The package url or purl is a URL string used to identify and locate a software package."
>
purl
</span>
</td>
<td class="two-col-right">
{{ package.package_url }}
{{ package.purl }}
</td>
</tr>
</tbody>
Expand All @@ -45,12 +45,7 @@

<div class="content ml-3 mr-3">
<div class="has-text-weight-bold ml-1 mb-0">
Affected by vulnerabilities
{% if impacted_vuln %}
({{ impacted_vuln|length }})
{% else %}
(0)
{% endif %}
Affected by vulnerabilities ({{ affected_by_vulnerabilities|length }})
</div>

<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
Expand All @@ -61,48 +56,43 @@
<th style="width: 225px;">Aliases</th>
</tr>
</thead>

<tbody>
{% if impacted_vuln %}
{% for vuln in impacted_vuln %}
<tr>
<td>
<a href="{{ vulnerability.get_absolute_url }}" target="_self">{{ vuln.vulnerability_id }}</a>
</td>
<td>
{{ vuln.summary }}
</td>
<td>
{% for alias in vuln.alias %}
{% if alias.url %}
<a href={{ alias.url }} target="_blank">{{ alias }}<i class="fa fa-external-link fa_link_custom"></i></a>
<br />
{% else %}
{{ alias }}
<br />
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
{% else %}
{% for vulnerability in affected_by_vulnerabilities %}
<tr>
<td>
<a href="{{ vulnerability.get_absolute_url }}" target="_self">{{ vulnerability.vulnerability_id }}</a>
</td>
<td>
{{ vulnerability.summary }}
</td>
<td>
{% for alias in vulnerability.alias %}
{% if alias.url %}
<a href={{ alias.url }} target="_blank">{{ alias }}<i class="fa fa-external-link fa_link_custom"></i></a>
<br />
{% else %}
{{ alias }}
<br />
{% endif %}
{% endfor %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">
This package is not affected by known vulnerabilities.
This package is not known to be affected by vulnerabilities.
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>

</table>
</div>

<div class="content ml-3 mr-3">
<div class="has-text-weight-bold ml-1 mb-0">
Fixing vulnerabilities
{% if resolved_vuln %}
({{ resolved_vuln|length }})
{% else %}
(0)
{% endif %}
Fixing vulnerabilities ({{ fixing_vulnerabilities|length }})
</div>

<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth">
Expand All @@ -113,38 +103,39 @@
<th style="width: 225px;">Aliases</th>
</tr>
</thead>

<tbody>
{% if resolved_vuln %}
{% for vuln in resolved_vuln %}
<tr>
<td>
<a href="{{ vuln.get_absolute_url }}" target="_self">{{ vuln.vulnerability_id }}</a>
</td>
<td>
{{ vuln.summary }}
</td>
<td>
{% for alias in vuln.alias %}
{% if alias.url %}
<a href={{ alias.url }} target="_blank">{{ alias }}<i class="fa fa-external-link fa_link_custom"></i></a>
<br />
{% else %}
{{ alias }}
<br />
{% endif %}
{% endfor %}
</td>
</tr>
{% endfor %}
{% else %}
{% for vulnerability in fixing_vulnerabilities %}
<tr>
<td>
<a href="{{ vulnerability.get_absolute_url }}" target="_self">{{ vulnerability.vulnerability_id }}</a>
</td>
<td>
{{ vulnerability.summary }}
</td>
<td>
{% for alias in vulnerability.alias %}
{% if alias.url %}
<a href={{ alias.url }} target="_blank">{{ alias }}<i class="fa fa-external-link fa_link_custom"></i></a>
<br />
{% else %}
{{ alias }}
<br />
{% endif %}
{% endfor %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">
This package is not known to fix vulnerabilities.
</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>

<br/>
</div>
</div>
</section>
Expand Down
4 changes: 2 additions & 2 deletions vulnerabilities/templates/package_search_box.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
<form
action="{% url 'package_search' %}"
method="get"
name="package_form"
name="package_search_form"
>
<div class="field has-addons mt-3">
<div class="control width-100-pct">
{{ package_form.search|add_class:"input" }}
{{ package_search_form.search|add_class:"input" }}
</div>
<div class="control">
<button class="button is-link" type="submit" id="submit_pkg">
Expand Down
Loading

0 comments on commit 681930a

Please sign in to comment.