|
10 | 10 | import csv |
11 | 11 | import datetime |
12 | 12 | import hashlib |
| 13 | +import json |
13 | 14 | import logging |
14 | 15 | import uuid |
15 | 16 | import xml.etree.ElementTree as ET |
|
68 | 69 | from vulnerabilities.importer import AdvisoryDataV2 |
69 | 70 | from vulnerabilities.severity_systems import EPSS |
70 | 71 | from vulnerabilities.severity_systems import SCORING_SYSTEMS |
71 | | -from vulnerabilities.utils import compute_patch_checksum |
| 72 | +from vulnerabilities.utils import compute_patch_checksum, normalize_list, normalize_text |
72 | 73 | from vulnerabilities.utils import normalize_purl |
73 | 74 | from vulnerabilities.utils import purl_to_dict |
74 | 75 | from vulnerablecode import __version__ as VULNERABLECODE_VERSION |
@@ -2988,11 +2989,11 @@ class AdvisoryV2(models.Model): |
2988 | 2989 | help_text="Weighted severity is the highest value calculated by multiplying each severity by its corresponding weight, divided by 10.", |
2989 | 2990 | ) |
2990 | 2991 |
|
2991 | | - # precedence = models.IntegerField( |
2992 | | - # null=True, |
2993 | | - # blank=True, |
2994 | | - # help_text="Precedence indicates the priority level of addressing a vulnerability based on its overall risk", |
2995 | | - # ) |
| 2992 | + precedence = models.IntegerField( |
| 2993 | + null=True, |
| 2994 | + blank=True, |
| 2995 | + help_text="Precedence indicates the priority of advisory from different datasources. It is determined based on the reliability of the datasource and how close it is to the source.", |
| 2996 | + ) |
2996 | 2997 |
|
2997 | 2998 | @property |
2998 | 2999 | def risk_score(self): |
@@ -3057,6 +3058,35 @@ def get_aliases(self): |
3057 | 3058 | Return a queryset of all Aliases for this vulnerability. |
3058 | 3059 | """ |
3059 | 3060 | return self.aliases.all() |
| 3061 | + |
| 3062 | + def compute_advisory_content(self): |
| 3063 | + """ |
| 3064 | + Compute a unique content hash for an advisory by normalizing its data and hashing it. |
| 3065 | +
|
| 3066 | + :param advisory: An Advisory object |
| 3067 | + :return: SHA-256 hash digest as content hash |
| 3068 | + """ |
| 3069 | + normalized_data = { |
| 3070 | + "summary": normalize_text(self.summary), |
| 3071 | + "impacted_packages": sorted( |
| 3072 | + [impact.to_dict() for impact in self.impacted_packages.all()], |
| 3073 | + key=lambda x: json.dumps(x, sort_keys=True), |
| 3074 | + ), |
| 3075 | + "patches": sorted( |
| 3076 | + [patch.to_patch_data().to_dict() for patch in self.patches.all()], |
| 3077 | + key=lambda x: json.dumps(x, sort_keys=True), |
| 3078 | + ), |
| 3079 | + "severities": sorted( |
| 3080 | + [sev.to_vulnerability_severity_data().to_dict() for sev in self.severities.all()], |
| 3081 | + key=lambda x: (x.get("system"), x.get("value")), |
| 3082 | + ), |
| 3083 | + "weaknesses": normalize_list([weakness.cwe_id for weakness in self.weaknesses.all()]), |
| 3084 | + } |
| 3085 | + |
| 3086 | + normalized_json = json.dumps(normalized_data, separators=(",", ":"), sort_keys=True) |
| 3087 | + content_hash = hashlib.sha256(normalized_json.encode("utf-8")).hexdigest() |
| 3088 | + |
| 3089 | + return content_hash |
3060 | 3090 |
|
3061 | 3091 | alias = get_aliases |
3062 | 3092 |
|
|
0 commit comments