@@ -1323,7 +1323,7 @@ def url(self):
13231323 return f"https://github.com/nodejs/security-wg/blob/main/vuln/npm/{ id } .json"
13241324
13251325
1326- class AdvisoryQuerySet (BaseQuerySet ):
1326+ class AdvisoryV2QuerySet (BaseQuerySet ):
13271327 def search (query ):
13281328 """
13291329 This function will take a string as an input, the string could be an alias or an advisory ID or
@@ -1337,6 +1337,20 @@ def search(query):
13371337 ).distinct ()
13381338
13391339
1340+ class AdvisoryQuerySet (BaseQuerySet ):
1341+ def search (query ):
1342+ """
1343+ This function will take a string as an input, the string could be an alias or an advisory ID or
1344+ something in the advisory description.
1345+ """
1346+ return Advisory .objects .filter (
1347+ Q (advisory_id__icontains = query )
1348+ | Q (aliases__alias__icontains = query )
1349+ | Q (summary__icontains = query )
1350+ | Q (references__url__icontains = query )
1351+ ).distinct ()
1352+
1353+
13401354# FIXME: Remove when migration from Vulnerability to Advisory is completed
13411355class Advisory (models .Model ):
13421356 """
@@ -1820,6 +1834,60 @@ class Meta:
18201834 abstract = True
18211835
18221836
1837+ class CodeChangeV2 (models .Model ):
1838+ """
1839+ Abstract base model representing a change in code, either introducing or fixing a vulnerability.
1840+ This includes details about commits, patches, and related metadata.
1841+
1842+ We are tracking commits, pulls and downloads as references to the code change. The goal is to
1843+ keep track and store the actual code patch in the ``patch`` field. When not available the patch
1844+ will be inferred from these references using improvers.
1845+ """
1846+
1847+ commits = models .JSONField (
1848+ blank = True ,
1849+ default = list ,
1850+ help_text = "List of commit identifiers using VCS URLs associated with the code change." ,
1851+ )
1852+ pulls = models .JSONField (
1853+ blank = True ,
1854+ default = list ,
1855+ help_text = "List of pull request URLs associated with the code change." ,
1856+ )
1857+ downloads = models .JSONField (
1858+ blank = True , default = list , help_text = "List of download URLs for the patched code."
1859+ )
1860+ patch = models .TextField (
1861+ blank = True , null = True , help_text = "The code change as a patch in unified diff format."
1862+ )
1863+ base_package_version = models .ForeignKey (
1864+ "PackageV2" ,
1865+ null = True ,
1866+ blank = True ,
1867+ on_delete = models .SET_NULL ,
1868+ related_name = "codechanges_v2" ,
1869+ help_text = "The base package version to which this code change applies." ,
1870+ )
1871+ notes = models .TextField (
1872+ blank = True , null = True , help_text = "Notes or instructions about this code change."
1873+ )
1874+ references = models .JSONField (
1875+ blank = True , default = list , help_text = "URL references related to this code change."
1876+ )
1877+ is_reviewed = models .BooleanField (
1878+ default = False , help_text = "Indicates if this code change has been reviewed."
1879+ )
1880+ created_at = models .DateTimeField (
1881+ auto_now_add = True , help_text = "Timestamp indicating when this code change was created."
1882+ )
1883+ updated_at = models .DateTimeField (
1884+ auto_now = True , help_text = "Timestamp indicating when this code change was last updated."
1885+ )
1886+
1887+ class Meta :
1888+ abstract = True
1889+
1890+
18231891class CodeFix (CodeChange ):
18241892 """
18251893 A code fix is a code change that addresses a vulnerability and is associated:
@@ -1844,6 +1912,35 @@ class CodeFix(CodeChange):
18441912 )
18451913
18461914
1915+ class CodeFixV2 (CodeChangeV2 ):
1916+ """
1917+ A code fix is a code change that addresses a vulnerability and is associated:
1918+ - with a specific advisory
1919+ - package that has been affected
1920+ - optionally with a specific fixing package version when it is known
1921+ """
1922+
1923+ advisory = models .ForeignKey (
1924+ "AdvisoryV2" ,
1925+ on_delete = models .CASCADE ,
1926+ related_name = "code_fix_v2" ,
1927+ help_text = "The affected package version to which this code fix applies." ,
1928+ )
1929+
1930+ affected_package = models .ForeignKey (
1931+ "PackageV2" , on_delete = models .CASCADE , related_name = "code_fix_v2_affected"
1932+ )
1933+
1934+ fixed_package = models .ForeignKey (
1935+ "PackageV2" ,
1936+ null = True ,
1937+ blank = True ,
1938+ on_delete = models .SET_NULL ,
1939+ related_name = "code_fix_v2_fixed" ,
1940+ help_text = "The fixing package version with this code fix" ,
1941+ )
1942+
1943+
18471944class PipelineRun (models .Model ):
18481945 """The Database representation of a pipeline execution."""
18491946
@@ -2451,6 +2548,23 @@ class AdvisoryV2(models.Model):
24512548 into structured data
24522549 """
24532550
2551+ # This is similar to a type or a namespace
2552+ datasource_id = models .CharField (
2553+ max_length = 100 ,
2554+ blank = False ,
2555+ null = False ,
2556+ help_text = "Unique ID for the datasource used for this advisory ." "e.g.: nginx_importer_v2" ,
2557+ )
2558+
2559+ avid = models .CharField (
2560+ max_length = 500 ,
2561+ blank = False ,
2562+ null = False ,
2563+ help_text = "Unique ID for the datasource used for this advisory ."
2564+ "e.g.: pysec_importer_v2/PYSEC-2020-2233" ,
2565+ )
2566+
2567+ # This is similar to a name
24542568 advisory_id = models .CharField (
24552569 max_length = 50 ,
24562570 blank = False ,
@@ -2460,13 +2574,27 @@ class AdvisoryV2(models.Model):
24602574 "such as PYSEC-2020-2233" ,
24612575 )
24622576
2577+ # This is similar to a version
24632578 unique_content_id = models .CharField (
24642579 max_length = 64 ,
24652580 blank = False ,
24662581 null = False ,
24672582 unique = True ,
24682583 help_text = "A 64 character unique identifier for the content of the advisory since we use sha256 as hex" ,
24692584 )
2585+ url = models .URLField (
2586+ blank = False ,
2587+ null = False ,
2588+ help_text = "Link to the advisory on the upstream website" ,
2589+ )
2590+
2591+ # TODO: Have a mapping that gives datasource class by datasource ID
2592+ # Get label from datasource class
2593+ # Remove this from model
2594+ # In the UI - Use label
2595+ # In the API - Use datasource_id
2596+ # Have an API endpoint for all info for datasources - show license, label
2597+
24702598 summary = models .TextField (
24712599 blank = True ,
24722600 )
@@ -2497,18 +2625,6 @@ class AdvisoryV2(models.Model):
24972625 date_imported = models .DateTimeField (
24982626 blank = True , null = True , help_text = "UTC Date on which the advisory was imported"
24992627 )
2500- # TODO: Rename to datasource ID
2501- datasource_ID = models .CharField (
2502- max_length = 100 ,
2503- help_text = "Fully qualified name of the importer prefixed with the"
2504- "module name importing the advisory. Eg:"
2505- "nginx_importer_v2" ,
2506- )
2507- url = models .URLField (
2508- blank = False ,
2509- null = False ,
2510- help_text = "Link to the advisory on the upstream website" ,
2511- )
25122628
25132629 affecting_packages = models .ManyToManyField (
25142630 "PackageV2" ,
@@ -2558,7 +2674,8 @@ def risk_score(self):
25582674 objects = AdvisoryQuerySet .as_manager ()
25592675
25602676 class Meta :
2561- ordering = ["date_published" , "unique_content_id" ]
2677+ unique_together = ["datasource_id" , "advisory_id" , "unique_content_id" ]
2678+ ordering = ["datasource_id" , "advisory_id" , "date_published" , "unique_content_id" ]
25622679
25632680 def save (self , * args , ** kwargs ):
25642681 self .full_clean ()
0 commit comments