Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CWE support in the API #1116

Merged
merged 3 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion vulnerabilities/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityReference
from vulnerabilities.models import VulnerabilitySeverity
from vulnerabilities.models import Weakness
from vulnerabilities.models import get_purl_query_lookups
from vulnerabilities.throttling import StaffUserRateThrottle

Expand Down Expand Up @@ -121,6 +122,25 @@ class Meta:
fields = ["url", "vulnerability_id", "summary", "references", "fixed_packages", "aliases"]


class WeaknessSerializer(serializers.HyperlinkedModelSerializer):
"""
Used for nesting inside weakness focused APIs.
"""

class Meta:
model = Weakness
fields = ["cwe_id", "name", "description"]

def to_representation(self, instance):
"""
Override to include 'weakness' only if it is not None.
"""
representation = super().to_representation(instance)
if instance.weakness is None:
return None
return representation


class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):
fixed_packages = MinimalPackageSerializer(
many=True, source="filtered_fixed_packages", read_only=True
Expand All @@ -129,6 +149,16 @@ class VulnerabilitySerializer(serializers.HyperlinkedModelSerializer):

references = VulnerabilityReferenceSerializer(many=True, source="vulnerabilityreference_set")
aliases = AliasSerializer(many=True, source="alias")
weaknesses = WeaknessSerializer(many=True)

def to_representation(self, instance):
representation = super().to_representation(instance)

# Exclude None values from the weaknesses list
weaknesses = representation.get("weaknesses", [])
representation["weaknesses"] = [weakness for weakness in weaknesses if weakness is not None]

return representation

class Meta:
model = Vulnerability
Expand All @@ -140,6 +170,7 @@ class Meta:
"fixed_packages",
"affected_packages",
"references",
"weaknesses",
]


Expand Down Expand Up @@ -491,11 +522,12 @@ def get_queryset(self):
to a custom attribute `filtered_fixed_packages`
"""
return Vulnerability.objects.prefetch_related(
"weaknesses",
Prefetch(
"packages",
queryset=self.get_fixed_packages_qs(),
to_attr="filtered_fixed_packages",
)
),
)

serializer_class = VulnerabilitySerializer
Expand Down
3 changes: 3 additions & 0 deletions vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ def description(self):
"""Return the weakness's description."""
return self.weakness.description if self.weakness else ""

def to_dict(self):
return {"cwe_id": self.cwe_id, "name": self.name, "description": self.description}


class VulnerabilityReferenceQuerySet(BaseQuerySet):
def for_cpe(self):
Expand Down
21 changes: 21 additions & 0 deletions vulnerabilities/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from vulnerabilities.models import Vulnerability
from vulnerabilities.models import VulnerabilityReference
from vulnerabilities.models import VulnerabilityRelatedReference
from vulnerabilities.models import Weakness

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
TEST_DATA = os.path.join(BASE_DIR, "test_data")
Expand Down Expand Up @@ -197,6 +198,12 @@ def setUp(self):
PackageRelatedVulnerability.objects.create(
package=pkg, vulnerability=self.vulnerability, fix=True
)
self.weaknesses = Weakness.objects.create(cwe_id=119)
self.weaknesses.vulnerabilities.add(self.vulnerability)
self.invalid_weaknesses = Weakness.objects.create(
cwe_id=10000
) # cwe not present in weaknesses_db
self.invalid_weaknesses.vulnerabilities.add(self.vulnerability)

def test_api_status(self):
response = self.csrf_client.get("/api/vulnerabilities/")
Expand Down Expand Up @@ -232,6 +239,13 @@ def test_api_with_single_vulnerability(self):
],
"affected_packages": [],
"references": [],
"weaknesses": [
{
"cwe_id": 119,
"name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
"description": "The software performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
},
],
}

def test_api_with_single_vulnerability_with_filters(self):
Expand All @@ -253,6 +267,13 @@ def test_api_with_single_vulnerability_with_filters(self):
],
"affected_packages": [],
"references": [],
"weaknesses": [
{
"cwe_id": 119,
"name": "Improper Restriction of Operations within the Bounds of a Memory Buffer",
"description": "The software performs operations on a memory buffer, but it can read from or write to a memory location that is outside of the intended boundary of the buffer.",
},
],
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
"type": "rpm",
"namespace": "opensuse",
"name": "opera",
"version": null,
"qualifiers": null,
"subpath": null
"version": "",
"qualifiers": "",
"subpath": ""
},
"affected_version_range": "vers:rpm/<9.63-1.1",
"fixed_version": null
Expand All @@ -29,4 +29,4 @@
"weaknesses": [],
"url": ""
}
]
]
Loading