From 2469b99c2217d08c4772578469c6499b3d18ba7c Mon Sep 17 00:00:00 2001 From: Sarah Boyce <42296566+sarahboyce@users.noreply.github.com> Date: Wed, 6 May 2026 12:49:41 +0200 Subject: [checklists] Added RegexValidator to cve_year_number to ensure created issues are compatible with cve_sort_key. --- .../0007_alter_securityissue_cve_year_number.py | 29 +++++++++++++++++ checklists/models.py | 8 +++-- checklists/tests/test_models.py | 38 ++++++++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 checklists/migrations/0007_alter_securityissue_cve_year_number.py diff --git a/checklists/migrations/0007_alter_securityissue_cve_year_number.py b/checklists/migrations/0007_alter_securityissue_cve_year_number.py new file mode 100644 index 00000000..0bc2811c --- /dev/null +++ b/checklists/migrations/0007_alter_securityissue_cve_year_number.py @@ -0,0 +1,29 @@ +# Generated by Django 6.0.4 on 2026-05-07 02:32 + +import django.core.validators +from django.db import migrations, models + +import checklists.models + + +class Migration(migrations.Migration): + + dependencies = [ + ("checklists", "0006_remove_securityissue_attack_complexity_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="securityissue", + name="cve_year_number", + field=models.CharField( + default=checklists.models.get_cve_default, + max_length=1024, + unique=True, + validators=[ + django.core.validators.RegexValidator(regex="CVE-\\d{4}-\\d{4,5}") + ], + verbose_name="CVE ID", + ), + ), + ] diff --git a/checklists/models.py b/checklists/models.py index ad44dbf7..41b9a2b1 100644 --- a/checklists/models.py +++ b/checklists/models.py @@ -2,7 +2,7 @@ import datetime import json from django.contrib.auth.models import User -from django.core.validators import MaxValueValidator, MinValueValidator +from django.core.validators import MaxValueValidator, MinValueValidator, RegexValidator from django.db import models from django.db.models.functions import Cast, Substr from django.shortcuts import reverse @@ -467,7 +467,11 @@ class SecurityIssue(models.Model): choices=[(i, i) for i in ("DSF", "MITRE")], ) cve_year_number = models.CharField( - "CVE ID", max_length=1024, unique=True, default=get_cve_default + "CVE ID", + max_length=1024, + unique=True, + default=get_cve_default, + validators=[RegexValidator(regex=r"CVE-\d{4}-\d{4,5}")], ) objects = SecurityIssueManager() diff --git a/checklists/tests/test_models.py b/checklists/tests/test_models.py index 73ad4851..192df301 100644 --- a/checklists/tests/test_models.py +++ b/checklists/tests/test_models.py @@ -3,6 +3,7 @@ import re import zoneinfo from datetime import UTC, date, datetime +from django.core.exceptions import ValidationError from django.db import IntegrityError from django.template.loader import render_to_string from django.test import RequestFactory, TestCase, override_settings @@ -872,6 +873,43 @@ class GetCvssSeverityTests(TestCase): self.assertEqual(get_cvss_severity(10.0), "CRITICAL") +class SecurityIssueTestCase(TestCase): + def test_cve_year_number_invalid(self): + invalid_cve_numbers = [ + "CVE-2026-1", + "CVE-2026-XXXX", + "CVE-20026-1111", + ] + for cve in invalid_cve_numbers: + with self.subTest(cve=cve): + with self.assertRaises(ValidationError) as context: + SecurityIssue(cve_year_number=cve).full_clean() + + self.assertEqual( + context.exception.message_dict.get("cve_year_number"), + ["Enter a valid value."], + ) + + def test_cve_year_number_valid(self): + valid_cve_numbers = [ + "CVE-2026-12345", + "CVE-2026-1234", + ] + for cve in valid_cve_numbers: + with self.subTest(cve=cve): + # No ValidationError raised. + SecurityIssue(cve_year_number=cve).full_clean( + exclude=( + "summary", + "description", + "cve_type", + "impact", + "confirmed_at", + "reported_at", + ) + ) + + class CvssMetricsInCveDataTests(TestCase): factory = Factory() -- cgit v1.3