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 support for CWE #782

Merged
merged 2 commits into from
Jan 24, 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
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ yarl==1.7.2
zipp==3.8.0
dateparser==1.1.1
fetchcode==0.2.0

cwe2==2.0.0
drf-spectacular-sidecar==2022.10.1
drf-spectacular==0.24.2
coreapi==2.3.3
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ install_requires =
Markdown>=3.3.0
dateparser>=1.1.1
cvss>=2.4
cwe2>=2.0.0

# networking
GitPython>=3.1.17
Expand Down
1 change: 1 addition & 0 deletions vulnerabilities/import_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def process_advisories(advisory_datas: Iterable[AdvisoryData], importer_name: st
affected_packages=[pkg.to_dict() for pkg in data.affected_packages],
references=[ref.to_dict() for ref in data.references],
date_published=data.date_published,
weaknesses=data.weaknesses,
defaults={
"created_by": importer_name,
"date_collected": datetime.datetime.now(tz=datetime.timezone.utc),
Expand Down
3 changes: 3 additions & 0 deletions vulnerabilities/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ class AdvisoryData:
affected_packages: List[AffectedPackage] = dataclasses.field(default_factory=list)
references: List[Reference] = dataclasses.field(default_factory=list)
date_published: Optional[datetime.datetime] = None
weaknesses: List[int] = dataclasses.field(default_factory=list)

def __post_init__(self):
if self.date_published and not self.date_published.tzinfo:
Expand All @@ -258,6 +259,7 @@ def to_dict(self):
"affected_packages": [pkg.to_dict() for pkg in self.affected_packages],
"references": [ref.to_dict() for ref in self.references],
"date_published": self.date_published.isoformat() if self.date_published else None,
"weaknesses": self.weaknesses,
}

@classmethod
Expand All @@ -273,6 +275,7 @@ def from_dict(cls, advisory_data):
"date_published": datetime.datetime.fromisoformat(date_published)
if date_published
else None,
"weaknesses": advisory_data["weaknesses"],
}
return cls(**transformed)

Expand Down
21 changes: 21 additions & 0 deletions vulnerabilities/importers/nvd.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from vulnerabilities.importer import Importer
from vulnerabilities.importer import Reference
from vulnerabilities.importer import VulnerabilitySeverity
from vulnerabilities.utils import get_cwe_id
from vulnerabilities.utils import get_item


Expand Down Expand Up @@ -236,6 +237,25 @@ def is_related_to_hardware(self):
"""
return any(is_related_to_hardware(cpe) for cpe in self.cpes)

@property
def weaknesses(self):
"""
Return a list of CWE IDs like: [119, 189]
"""
weaknesses = []
for weaknesses_item in (
get_item(self.cve_item, "cve", "problemtype", "problemtype_data") or []
):
weaknesses_description = weaknesses_item.get("description") or []
for weaknesses_value in weaknesses_description:
cwe_id = (
weaknesses_value.get("value") if weaknesses_value.get("lang") == "en" else None
)
if cwe_id in ["NVD-CWE-Other", "NVD-CWE-noinfo"] or not cwe_id:
continue # Skip Invalid CWE
weaknesses.append(get_cwe_id(cwe_id))
return weaknesses

def to_advisory(self):
"""
Return an AdvisoryData object from this CVE item
Expand All @@ -245,6 +265,7 @@ def to_advisory(self):
summary=self.summary,
references=self.references,
date_published=dateparser.parse(self.cve_item.get("publishedDate")),
weaknesses=self.weaknesses,
)


Expand Down
6 changes: 6 additions & 0 deletions vulnerabilities/improve_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from vulnerabilities.models import VulnerabilityReference
from vulnerabilities.models import VulnerabilityRelatedReference
from vulnerabilities.models import VulnerabilitySeverity
from vulnerabilities.models import Weakness

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -133,6 +134,11 @@ def process_inferences(inferences: List[Inference], advisory: Advisory, improver
fix=True,
).update_or_create()

if inference.weaknesses and vulnerability:
for cwe_id in inference.weaknesses:
cwe_obj, created = Weakness.objects.get_or_create(cwe_id=cwe_id)
cwe_obj.vulnerabilities.add(vulnerability)
cwe_obj.save()
advisory.date_improved = datetime.now(timezone.utc)
advisory.save()

Expand Down
4 changes: 4 additions & 0 deletions vulnerabilities/improver.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Inference:
affected_purls: Optional[List[PackageURL]] = dataclasses.field(default_factory=list)
fixed_purl: PackageURL = None
references: List[Reference] = dataclasses.field(default_factory=list)
weaknesses: List[int] = dataclasses.field(default_factory=list)

def __post_init__(self):
if self.confidence > MAX_CONFIDENCE or self.confidence < 0:
Expand All @@ -54,6 +55,7 @@ def __post_init__(self):
or self.affected_purls
or self.fixed_purl
or self.references
or self.weaknesses
)

versionless_purls = []
Expand Down Expand Up @@ -82,6 +84,7 @@ def to_dict(self):
"affected_purls": [affected_purl.to_dict() for affected_purl in self.affected_purls],
"fixed_purl": self.fixed_purl.to_dict() if self.fixed_purl else None,
"references": [ref.to_dict() for ref in self.references],
"weaknesses": self.weaknesses,
}

@classmethod
Expand All @@ -97,6 +100,7 @@ def from_advisory_data(cls, advisory_data, confidence, fixed_purl, affected_purl
affected_purls=affected_purls or [],
fixed_purl=fixed_purl,
references=advisory_data.references,
weaknesses=advisory_data.weaknesses,
)


Expand Down
26 changes: 26 additions & 0 deletions vulnerabilities/migrations/0037_advisory_weaknesses_weakness.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 4.0.7 on 2023-01-01 20:25

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('vulnerabilities', '0036_alter_package_package_url_and_more'),
]

operations = [
migrations.AddField(
model_name='advisory',
name='weaknesses',
field=models.JSONField(blank=True, default=list, help_text='A list of CWE ids'),
),
migrations.CreateModel(
name='Weakness',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('cwe_id', models.IntegerField(help_text='CWE id')),
('vulnerabilities', models.ManyToManyField(related_name='weaknesses', to='vulnerabilities.vulnerability')),
],
),
]
26 changes: 25 additions & 1 deletion vulnerabilities/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import logging
from contextlib import suppress

from cwe2.database import Database
from django.contrib.auth import get_user_model
from django.contrib.auth.models import UserManager
from django.core import exceptions
Expand Down Expand Up @@ -250,6 +251,28 @@ def get_related_purls(self):
return [p.package_url for p in self.packages.distinct().all()]


class Weakness(models.Model):
pombredanne marked this conversation as resolved.
Show resolved Hide resolved
"""
A Common Weakness Enumeration model
"""

cwe_id = models.IntegerField(help_text="CWE id")
vulnerabilities = models.ManyToManyField(Vulnerability, related_name="weaknesses")
db = Database()

@property
def name(self):
"""Return the weakness's name."""
weakness = self.db.get(self.cwe_id)
return weakness.name

@property
def description(self):
"""Return the weakness's description."""
weakness = self.db.get(self.cwe_id)
return weakness.description


class VulnerabilityReferenceQuerySet(BaseQuerySet):
def for_cpe(self):
"""
Expand Down Expand Up @@ -662,7 +685,6 @@ def update_or_create(self):


class VulnerabilitySeverity(models.Model):

reference = models.ForeignKey(VulnerabilityReference, on_delete=models.CASCADE)

scoring_system_choices = tuple(
Expand Down Expand Up @@ -774,6 +796,7 @@ class Advisory(models.Model):
date_published = models.DateTimeField(
blank=True, null=True, help_text="UTC Date of publication of the advisory"
)
weaknesses = models.JSONField(blank=True, default=list, help_text="A list of CWE ids")
date_collected = models.DateTimeField(help_text="UTC Date on which the advisory was collected")
date_improved = models.DateTimeField(
blank=True,
Expand Down Expand Up @@ -806,6 +829,7 @@ def to_advisory_data(self) -> AdvisoryData:
affected_packages=[AffectedPackage.from_dict(pkg) for pkg in self.affected_packages],
references=[Reference.from_dict(ref) for ref in self.references],
date_published=self.date_published,
weaknesses=self.weaknesses,
)


Expand Down
27 changes: 26 additions & 1 deletion vulnerabilities/templates/vulnerability_details.html
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@
</table>
</div>


<div class="has-text-weight-bold tab-nested-div ml-1 mb-1 mt-6">
Fixed by packages ({{ fixed_by_packages|length }})
</div>
Expand Down Expand Up @@ -174,6 +173,32 @@
{% endif %}
</table>
</div>

<div class="has-text-weight-bold tab-nested-div ml-1 mb-1 mt-6">
Weaknesses ({{ weaknesses|length }})
</div>
<div class="tab-nested-div">
<table class="table is-bordered is-striped is-narrow is-hoverable is-fullwidth gray-header-border">
{% for weakness in weaknesses %}
<tr>
<td class="wrap-strings">CWE-{{ weakness.cwe_id }}</td>
<td class="wrap-strings">
<a href="https://cwe.mitre.org/data/definitions/{{ weakness.cwe_id }}.html" target="_blank"
title="CWE-{{ weakness.cwe_id }} : description: {{weakness.description}}">
{{ weakness.name }} <i class="fa fa-external-link fa_link_custom"></i>
</a>
</td>

</tr>
{% empty %}
<tr>
<td colspan="3">
There are no known CWE.
</td>
</tr>
{% endfor %}
</table>
</div>
</div>

<div class="tab-div content" data-content="references">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
]
}
],
"date_published": null
"date_published": null,
"weaknesses": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
]
}
],
"date_published": null
"date_published": null,
"weaknesses": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
]
}
],
"date_published": null
"date_published": null,
"weaknesses": []
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
]
}
],
"date_published": null
"date_published": null,
"weaknesses": []
}
Loading