@@ -1799,3 +1799,115 @@ class CodeFix(CodeChange):
17991799 related_name = "code_fix" ,
18001800 help_text = "The fixing package version with this code fix" ,
18011801 )
1802+
1803+
1804+ class AdvisoryReference (models .Model ):
1805+ """
1806+ A reference associated with an advisory.
1807+ """
1808+ advisory = models .ForeignKey (
1809+ 'Advisory' ,
1810+ on_delete = models .CASCADE ,
1811+ related_name = 'advisory_references' ,
1812+ )
1813+ reference_id = models .CharField (
1814+ max_length = 200 ,
1815+ blank = True ,
1816+ help_text = "External reference ID (e.g., CVE-ID, GHSA-ID)" ,
1817+ )
1818+ reference_type = models .CharField (
1819+ max_length = 100 ,
1820+ blank = True ,
1821+ help_text = "Type of reference (e.g., CVE, GHSA)" ,
1822+ )
1823+ url = models .URLField (
1824+ help_text = "URL of the reference" ,
1825+ )
1826+ severities = models .JSONField (
1827+ blank = True ,
1828+ default = list ,
1829+ help_text = "List of severity scores associated with this reference" ,
1830+ )
1831+
1832+ class Meta :
1833+ ordering = ['reference_id' , 'url' ]
1834+ unique_together = ['advisory' , 'reference_id' , 'url' ]
1835+
1836+ def to_reference (self ):
1837+ """Convert to importer.Reference object"""
1838+ from vulnerabilities .importer import Reference , VulnerabilitySeverity
1839+ return Reference (
1840+ reference_id = self .reference_id ,
1841+ reference_type = self .reference_type ,
1842+ url = self .url ,
1843+ severities = [VulnerabilitySeverity .from_dict (s ) for s in self .severities ],
1844+ )
1845+
1846+ @classmethod
1847+ def from_reference (cls , reference , advisory ):
1848+ """Create from importer.Reference object"""
1849+ return cls (
1850+ advisory = advisory ,
1851+ reference_id = reference .reference_id ,
1852+ reference_type = reference .reference_type ,
1853+ url = reference .url ,
1854+ severities = [s .to_dict () for s in reference .severities ],
1855+ )
1856+
1857+
1858+ class AdvisoryAffectedPackage (models .Model ):
1859+ """
1860+ A package affected by a vulnerability as described in an advisory.
1861+ """
1862+ advisory = models .ForeignKey (
1863+ 'Advisory' ,
1864+ on_delete = models .CASCADE ,
1865+ related_name = 'advisory_affected_packages' ,
1866+ )
1867+ package = PackageURLMixin ()
1868+ affected_version_range = models .CharField (
1869+ max_length = 1024 ,
1870+ blank = True ,
1871+ null = True ,
1872+ help_text = "Version range of affected versions" ,
1873+ )
1874+ fixed_version = models .CharField (
1875+ max_length = 100 ,
1876+ blank = True ,
1877+ null = True ,
1878+ help_text = "Version that fixes the vulnerability" ,
1879+ )
1880+
1881+ class Meta :
1882+ ordering = ['package' ]
1883+ unique_together = ['advisory' , 'package' , 'affected_version_range' , 'fixed_version' ]
1884+
1885+ def to_affected_package (self ):
1886+ """Convert to importer.AffectedPackage object"""
1887+ from vulnerabilities .importer import AffectedPackage
1888+ from univers .version_range import VersionRange
1889+ from univers .versions import Version
1890+
1891+ affected_range = None
1892+ if self .affected_version_range :
1893+ affected_range = VersionRange .from_string (self .affected_version_range )
1894+
1895+ fixed = None
1896+ if self .fixed_version :
1897+ fixed = Version (self .fixed_version )
1898+
1899+ return AffectedPackage (
1900+ package = self .package ,
1901+ affected_version_range = affected_range ,
1902+ fixed_version = fixed ,
1903+ )
1904+
1905+ @classmethod
1906+ def from_affected_package (cls , affected_package , advisory ):
1907+ """Create from importer.AffectedPackage object"""
1908+ return cls (
1909+ advisory = advisory ,
1910+ package = affected_package .package ,
1911+ affected_version_range = str (affected_package .affected_version_range ) if affected_package .affected_version_range else None ,
1912+ fixed_version = str (affected_package .fixed_version ) if affected_package .fixed_version else None ,
1913+ )
0 commit comments