diff --git a/devel/reports.py b/devel/reports.py index ca8e9369..8d67eb33 100644 --- a/devel/reports.py +++ b/devel/reports.py @@ -21,8 +21,8 @@ def __init__(self, href, title, desc): self.desc = desc def __str__(self): - link = '%s' - return format_html(link % (self.href, self.title, self.desc)) + return format_html('{desc}', + href=self.href, title=self.title, desc=self.desc) def linkify_non_reproducible_packages(statuses): diff --git a/devel/tests/test_reports.py b/devel/tests/test_reports.py index 96b85a74..62729deb 100644 --- a/devel/tests/test_reports.py +++ b/devel/tests/test_reports.py @@ -1,6 +1,7 @@ from django.contrib.auth.models import User from django.test import TransactionTestCase +from devel.reports import Linkify from packages.models import PackageRelation @@ -98,3 +99,12 @@ def test_report_pkgbases_filtered_by_maintainer(self): f'/devel/reports/old/{self.user.username}/pkgbases/') self.assertEqual(response.status_code, 200) self.assertEqual(response.content.decode().strip(), 'linux') + + +def test_linkify_escapes_html(): + link = Linkify(href='">', title='', desc='xss') + result = str(link) + assert '') + return format_html('', link=link) @register.simple_tag diff --git a/news/views.py b/news/views.py index a8009427..b961afcc 100644 --- a/news/views.py +++ b/news/views.py @@ -1,5 +1,6 @@ from django import forms from django.conf import settings +from django.contrib.auth.decorators import login_required from django.core.mail import EmailMessage from django.http import HttpResponse from django.shortcuts import get_object_or_404, redirect @@ -82,6 +83,7 @@ def view_redirect(request, object_id): return redirect(newsitem, permanent=True) +@login_required @require_POST def preview(request): data = request.POST.get('data', '') diff --git a/pyproject.toml b/pyproject.toml index 1c0e4217..90d880a6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ requires-python = ">=3.13" dependencies = [ "bencode-py==4.1.0", "bleach==6.4.0", - "django==5.2.16", + "django==5.2.17", "django-countries==8.2.0", "django-csp==4.0", "django-prometheus==2.5.0", diff --git a/sitestatic/homepage.js b/sitestatic/homepage.js index 35dfb0ea..d6e17828 100644 --- a/sitestatic/homepage.js +++ b/sitestatic/homepage.js @@ -51,11 +51,24 @@ ul.style.display = "block"; const fragment = document.createDocumentFragment(); - for (let i = 0; i < pkgs.length; i++) { + for (const pkgname of pkgs) { const item = document.createElement("li"); - const text = pkgs[i].replace(letter, `${letter}`); - item.innerHTML = `${text}`; - item.setAttribute('data-value', pkgs[i]); + const anchor = document.createElement("a"); + anchor.href = "#"; + + const matchIndex = pkgname.indexOf(letter); + if (matchIndex !== -1) { + anchor.appendChild(document.createTextNode(pkgname.slice(0, matchIndex))); + const bold = document.createElement("b"); + bold.textContent = pkgname.slice(matchIndex, matchIndex + letter.length); + anchor.appendChild(bold); + anchor.appendChild(document.createTextNode(pkgname.slice(matchIndex + letter.length))); + } else { + anchor.textContent = pkgname; + } + + item.appendChild(anchor); + item.setAttribute('data-value', pkgname); fragment.appendChild(item); } diff --git a/uv.lock b/uv.lock index 810eaf1e..a93fcebf 100644 --- a/uv.lock +++ b/uv.lock @@ -80,7 +80,7 @@ smtp = [ requires-dist = [ { name = "bencode-py", specifier = "==4.1.0" }, { name = "bleach", specifier = "==6.4.0" }, - { name = "django", specifier = "==5.2.16" }, + { name = "django", specifier = "==5.2.17" }, { name = "django-countries", specifier = "==8.2.0" }, { name = "django-csp", specifier = "==4.0" }, { name = "django-ninja", specifier = "==1.6.2" }, @@ -351,16 +351,16 @@ wheels = [ [[package]] name = "django" -version = "5.2.16" +version = "5.2.17" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "asgiref" }, { name = "sqlparse" }, { name = "tzdata", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a9/26/889449d521ae508b26de715954faecd8bcf3f740affb81b2d146a83b42a5/django-5.2.16.tar.gz", hash = "sha256:59ea02020c3136fce14bef0bbece21a10a4febef5eed1c51c22ae468efa22200", size = 10890894, upload-time = "2026-07-07T13:52:17.005Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/d8/43e9d000519adceb189620b6869ff88031e046df91c2e9da72f8f6918399/django-5.2.17.tar.gz", hash = "sha256:9d4d93be539a18ab80d058eb515900e10951e04c537c5a6b394fc49528d3251f", size = 10889740, upload-time = "2026-08-04T15:04:03.173Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/13/1e5e3e4c15dcecb04281b3cb2a46a4670e1cef131068e202f6040df19224/django-5.2.16-py3-none-any.whl", hash = "sha256:04f354bf9d807a86ad1a8392fe3808d362358a8eafc322848e0e43e59b24371d", size = 8311943, upload-time = "2026-07-07T13:52:11.223Z" }, + { url = "https://files.pythonhosted.org/packages/df/f8/ce120525ca78f12b07daf65786679c5d0b54a75285a8958d3ae55e39da35/django-5.2.17-py3-none-any.whl", hash = "sha256:f04fb3b36ee119e1af4fa1d397d5fd6cf12700f49321e84d4f4c642c5b1973db", size = 8315563, upload-time = "2026-08-04T15:03:59.1Z" }, ] [[package]]