Skip to content

Commit d1673f3

Browse files
refactor(finding): move CWE helpers to dojo/finding/cwe.py
Move cwe_number, cwe_label, parse_cwes, finding_cwe_labels out of finding/vulnerability_id.py into a dedicated finding/cwe.py (CWEs are a weakness class, distinct from vulnerability identifiers). resolve_vulnerability_id_type stays in vulnerability_id.py. All importers updated.
1 parent f9ca488 commit d1673f3

13 files changed

Lines changed: 64 additions & 60 deletions

File tree

dojo/db_migrations/0280_backfill_finding_cwe.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from django.db import migrations
22

3-
from dojo.finding.vulnerability_id import cwe_label
3+
from dojo.finding.cwe import cwe_label
44

55
BATCH_SIZE = 1000
66

dojo/finding/api/serializer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
import dojo.finding.helper as finding_helper
1616
from dojo.authorization.authorization import user_has_permission
1717
from dojo.celery_dispatch import dojo_dispatch_task
18+
from dojo.finding.cwe import cwe_label, cwe_number
1819
from dojo.finding.helper import (
1920
save_cwes,
2021
save_endpoints_template,
2122
save_vulnerability_ids,
2223
save_vulnerability_ids_template,
2324
)
2425
from dojo.finding.models import BurpRawRequestResponse
25-
from dojo.finding.vulnerability_id import cwe_label, cwe_number
2626
from dojo.jira import services as jira_services
2727
from dojo.jira.api.serializers import JIRAIssueSerializer
2828
from dojo.location.models import LocationFindingReference

dojo/finding/cwe.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Pure helpers for CWE identifiers (no model imports, safe to import anywhere)."""
2+
3+
4+
def cwe_number(value) -> int | None:
5+
"""
6+
``"CWE-79"`` / ``"79"`` / ``79`` -> ``79`` (positive int, case-insensitive); anything else -> ``None``.
7+
8+
CWE numbers are positive, so ``0`` (Finding.cwe's "unset" sentinel) and non-numeric input -> ``None``.
9+
"""
10+
if value is None:
11+
return None
12+
token = str(value).strip().upper().removeprefix("CWE-")
13+
if not token.isdigit():
14+
return None
15+
number = int(token)
16+
return number if number > 0 else None
17+
18+
19+
def cwe_label(value) -> str | None:
20+
"""``79`` / ``"79"`` / ``"CWE-79"`` -> ``"CWE-79"`` (canonical, case-insensitive); invalid -> ``None``."""
21+
number = cwe_number(value)
22+
return f"CWE-{number}" if number is not None else None
23+
24+
25+
def parse_cwes(text: str | None) -> list[str]:
26+
"""
27+
Parse CWEs from user text (one per line or comma-separated) into canonical ``CWE-<n>`` labels.
28+
29+
Accepts ``89`` or ``CWE-89`` (case-insensitive); ignores anything non-numeric; deduplicates.
30+
"""
31+
result: list[str] = []
32+
for token in (text or "").replace(",", "\n").split():
33+
label = cwe_label(token)
34+
if label is not None and label not in result:
35+
result.append(label)
36+
return result
37+
38+
39+
def finding_cwe_labels(cwe, unsaved_cwes=None) -> list[str]:
40+
"""Canonical ``CWE-<n>`` labels for a finding: the primary ``cwe`` first, then extras, deduplicated."""
41+
labels: list[str] = []
42+
primary = cwe_label(cwe)
43+
if primary is not None:
44+
labels.append(primary)
45+
for extra in unsaved_cwes or []:
46+
label = cwe_label(extra)
47+
if label is not None:
48+
labels.append(label)
49+
return list(dict.fromkeys(labels))

dojo/finding/helper.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
from dojo.celery import app
2020
from dojo.endpoint.utils import endpoint_get_or_create, save_endpoints_to_add
2121
from dojo.file_uploads.helper import delete_related_files
22+
from dojo.finding.cwe import finding_cwe_labels
2223
from dojo.finding.deduplication import (
2324
dedupe_batch_of_findings,
2425
do_dedupe_finding_task_internal,
2526
do_false_positive_history,
2627
do_false_positive_history_batch,
2728
get_finding_models_for_deduplication,
2829
)
29-
from dojo.finding.vulnerability_id import finding_cwe_labels, resolve_vulnerability_id_type
30+
from dojo.finding.vulnerability_id import resolve_vulnerability_id_type
3031
from dojo.jira import services as jira_services
3132
from dojo.location.models import Location
3233
from dojo.location.status import FindingLocationStatus

dojo/finding/models.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
from titlecase import titlecase
2323

2424
from dojo.base_models.base import BaseModel
25-
from dojo.finding.vulnerability_id import cwe_label, finding_cwe_labels, resolve_vulnerability_id_type
25+
from dojo.finding.cwe import cwe_label, finding_cwe_labels
26+
from dojo.finding.vulnerability_id import resolve_vulnerability_id_type
2627

2728
# get_current_date/tomorrow/copy_model_util are defined early in dojo.models, before the
2829
# re-export that loads this module — so this resolves despite the partial circular load, and

dojo/finding/ui/forms.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from tagulous.forms import TagField
88

99
from dojo.endpoint.utils import validate_endpoints_to_add
10+
from dojo.finding.cwe import cwe_number, parse_cwes
1011
from dojo.finding.queries import get_authorized_findings
11-
from dojo.finding.vulnerability_id import cwe_number, parse_cwes
1212
from dojo.jira import services as jira_services
1313
from dojo.location.models import Location
1414
from dojo.location.utils import validate_locations_to_add

dojo/finding/vulnerability_id.py

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,6 @@
11
"""Pure helpers for vulnerability identifiers (no model imports, safe to import anywhere)."""
22

33

4-
def cwe_number(value) -> int | None:
5-
"""
6-
``"CWE-79"`` / ``"79"`` / ``79`` -> ``79`` (positive int, case-insensitive); anything else -> ``None``.
7-
8-
CWE numbers are positive, so ``0`` (Finding.cwe's "unset" sentinel) and non-numeric input -> ``None``.
9-
"""
10-
if value is None:
11-
return None
12-
token = str(value).strip().upper().removeprefix("CWE-")
13-
if not token.isdigit():
14-
return None
15-
number = int(token)
16-
return number if number > 0 else None
17-
18-
19-
def cwe_label(value) -> str | None:
20-
"""``79`` / ``"79"`` / ``"CWE-79"`` -> ``"CWE-79"`` (canonical, case-insensitive); invalid -> ``None``."""
21-
number = cwe_number(value)
22-
return f"CWE-{number}" if number is not None else None
23-
24-
25-
def parse_cwes(text: str | None) -> list[str]:
26-
"""
27-
Parse CWEs from user text (one per line or comma-separated) into canonical ``CWE-<n>`` labels.
28-
29-
Accepts ``89`` or ``CWE-89`` (case-insensitive); ignores anything non-numeric; deduplicates.
30-
"""
31-
result: list[str] = []
32-
for token in (text or "").replace(",", "\n").split():
33-
label = cwe_label(token)
34-
if label is not None and label not in result:
35-
result.append(label)
36-
return result
37-
38-
39-
def finding_cwe_labels(cwe, unsaved_cwes=None) -> list[str]:
40-
"""Canonical ``CWE-<n>`` labels for a finding: the primary ``cwe`` first, then extras, deduplicated."""
41-
labels: list[str] = []
42-
primary = cwe_label(cwe)
43-
if primary is not None:
44-
labels.append(primary)
45-
for extra in unsaved_cwes or []:
46-
label = cwe_label(extra)
47-
if label is not None:
48-
labels.append(label)
49-
return list(dict.fromkeys(labels))
50-
51-
524
def resolve_vulnerability_id_type(vulnerability_id: str | None) -> str | None:
535
"""
546
Autodetect the type from the id's leading prefix (the part before the first ``-``).

dojo/importers/base_importer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212

1313
import dojo.finding.helper as finding_helper
1414
import dojo.risk_acceptance.helper as ra_helper
15-
from dojo.finding.vulnerability_id import finding_cwe_labels, resolve_vulnerability_id_type
15+
from dojo.finding.cwe import finding_cwe_labels
16+
from dojo.finding.vulnerability_id import resolve_vulnerability_id_type
1617
from dojo.importers.options import ImporterOptions
1718
from dojo.jira.services import is_keep_in_sync
1819
from dojo.location.models import Location

dojo/management/commands/migrate_cwe.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from django.core.management.base import BaseCommand
44

5-
from dojo.finding.vulnerability_id import cwe_label
5+
from dojo.finding.cwe import cwe_label
66
from dojo.models import Finding, Finding_CWE
77

88
logger = logging.getLogger(__name__)

dojo/tools/generic/csv_parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from dateutil.parser import parse
77
from django.conf import settings
88

9-
from dojo.finding.vulnerability_id import cwe_number, parse_cwes
9+
from dojo.finding.cwe import cwe_number, parse_cwes
1010
from dojo.models import Endpoint, Finding
1111
from dojo.tools.locations import LocationData
1212

0 commit comments

Comments
 (0)