@@ -1799,3 +1799,115 @@ class CodeFix(CodeChange):
1799
1799
related_name = "code_fix" ,
1800
1800
help_text = "The fixing package version with this code fix" ,
1801
1801
)
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