@@ -1323,7 +1323,7 @@ def url(self):
1323
1323
return f"https://github.com/nodejs/security-wg/blob/main/vuln/npm/{ id } .json"
1324
1324
1325
1325
1326
- class AdvisoryQuerySet (BaseQuerySet ):
1326
+ class AdvisoryV2QuerySet (BaseQuerySet ):
1327
1327
def search (query ):
1328
1328
"""
1329
1329
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):
1337
1337
).distinct ()
1338
1338
1339
1339
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
+
1340
1354
# FIXME: Remove when migration from Vulnerability to Advisory is completed
1341
1355
class Advisory (models .Model ):
1342
1356
"""
@@ -1820,6 +1834,60 @@ class Meta:
1820
1834
abstract = True
1821
1835
1822
1836
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
+
1823
1891
class CodeFix (CodeChange ):
1824
1892
"""
1825
1893
A code fix is a code change that addresses a vulnerability and is associated:
@@ -1844,6 +1912,35 @@ class CodeFix(CodeChange):
1844
1912
)
1845
1913
1846
1914
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
+
1847
1944
class PipelineRun (models .Model ):
1848
1945
"""The Database representation of a pipeline execution."""
1849
1946
@@ -2451,6 +2548,23 @@ class AdvisoryV2(models.Model):
2451
2548
into structured data
2452
2549
"""
2453
2550
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
2454
2568
advisory_id = models .CharField (
2455
2569
max_length = 50 ,
2456
2570
blank = False ,
@@ -2460,13 +2574,27 @@ class AdvisoryV2(models.Model):
2460
2574
"such as PYSEC-2020-2233" ,
2461
2575
)
2462
2576
2577
+ # This is similar to a version
2463
2578
unique_content_id = models .CharField (
2464
2579
max_length = 64 ,
2465
2580
blank = False ,
2466
2581
null = False ,
2467
2582
unique = True ,
2468
2583
help_text = "A 64 character unique identifier for the content of the advisory since we use sha256 as hex" ,
2469
2584
)
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
+
2470
2598
summary = models .TextField (
2471
2599
blank = True ,
2472
2600
)
@@ -2497,18 +2625,6 @@ class AdvisoryV2(models.Model):
2497
2625
date_imported = models .DateTimeField (
2498
2626
blank = True , null = True , help_text = "UTC Date on which the advisory was imported"
2499
2627
)
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
- )
2512
2628
2513
2629
affecting_packages = models .ManyToManyField (
2514
2630
"PackageV2" ,
@@ -2558,7 +2674,8 @@ def risk_score(self):
2558
2674
objects = AdvisoryQuerySet .as_manager ()
2559
2675
2560
2676
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" ]
2562
2679
2563
2680
def save (self , * args , ** kwargs ):
2564
2681
self .full_clean ()
0 commit comments