Skip to content

Commit 470fc75

Browse files
committed
Rename and update DetectionRule model
Update sigma rules improver Signed-off-by: ziad hany <ziadhany2016@gmail.com>
1 parent 749b9e8 commit 470fc75

File tree

4 files changed

+132
-41
lines changed

4 files changed

+132
-41
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Generated by Django 4.2.25 on 2025-12-03 02:30
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("vulnerabilities", "0104_advisorydetectionrule"),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name="DetectionRule",
16+
fields=[
17+
(
18+
"id",
19+
models.AutoField(
20+
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
21+
),
22+
),
23+
(
24+
"rule_type",
25+
models.CharField(
26+
choices=[
27+
("yara", "Yara"),
28+
("yara-x", "Yara-X"),
29+
("sigma", "Sigma"),
30+
("clamav", "ClamAV"),
31+
("suricata", "Suricata"),
32+
],
33+
help_text="The type of the detection rule content (e.g., YARA, Sigma).",
34+
max_length=50,
35+
),
36+
),
37+
(
38+
"source_url",
39+
models.URLField(
40+
help_text="URL to the original source or reference for this rule.",
41+
max_length=1024,
42+
),
43+
),
44+
(
45+
"rule_metadata",
46+
models.JSONField(
47+
blank=True,
48+
help_text="Additional structured data such as tags, or author information.",
49+
null=True,
50+
),
51+
),
52+
(
53+
"rule_text",
54+
models.TextField(help_text="The content of the detection signature."),
55+
),
56+
(
57+
"advisory",
58+
models.ForeignKey(
59+
blank=True,
60+
null=True,
61+
on_delete=django.db.models.deletion.SET_NULL,
62+
related_name="detection_rules",
63+
to="vulnerabilities.advisoryv2",
64+
),
65+
),
66+
],
67+
),
68+
migrations.DeleteModel(
69+
name="AdvisoryDetectionRule",
70+
),
71+
]

vulnerabilities/models.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3416,29 +3416,43 @@ class Meta:
34163416
unique_together = ("commit_hash", "vcs_url")
34173417

34183418

3419-
class AdvisoryDetectionRule(models.Model):
3419+
class DetectionRuleTypes(models.TextChoices):
3420+
"""Defines the supported formats for security detection rules."""
3421+
3422+
YARA = "yara", "Yara"
3423+
YARA_X = "yara-x", "Yara-X"
3424+
SIGMA = "sigma", "Sigma"
3425+
CLAMAV = "clamav", "ClamAV"
3426+
SURICATA = "suricata", "Suricata"
3427+
3428+
3429+
class DetectionRule(models.Model):
34203430
"""
3421-
A detection rule (YARA, Sigma, ClamAV) linked to an advisory.
3431+
A Detection Rule is code used to identify malicious activity or security threats.
34223432
"""
34233433

3424-
RULE_TYPES = [
3425-
("yara", "YARA"),
3426-
("sigma", "Sigma Detection Rule"),
3427-
("clamav", "ClamAV Signature"),
3428-
]
3434+
rule_type = models.CharField(
3435+
max_length=50,
3436+
choices=DetectionRuleTypes.choices,
3437+
help_text="The type of the detection rule content (e.g., YARA, Sigma).",
3438+
)
34293439

3430-
advisory = models.ForeignKey(
3431-
AdvisoryV2,
3432-
related_name="detection_rules",
3433-
on_delete=models.CASCADE,
3440+
source_url = models.URLField(
3441+
max_length=1024, help_text="URL to the original source or reference for this rule."
34343442
)
34353443

3436-
rule_text = models.TextField(help_text="Full text of the detection rule, script, or signature.")
3444+
rule_metadata = models.JSONField(
3445+
null=True,
3446+
blank=True,
3447+
help_text="Additional structured data such as tags, or author information.",
3448+
)
34373449

3438-
rule_type = models.CharField(max_length=100, choices=RULE_TYPES, blank=True)
3450+
rule_text = models.TextField(help_text="The content of the detection signature.")
34393451

3440-
source_url = models.URLField(
3452+
advisory = models.ForeignKey(
3453+
AdvisoryV2,
3454+
related_name="detection_rules",
3455+
on_delete=models.SET_NULL,
34413456
null=True,
34423457
blank=True,
3443-
help_text="URL or reference to the source of the rule (vendor feed, GitHub repo, etc.).",
34443458
)

vulnerabilities/pipelines/v2_improvers/sigma_rules.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99

1010
from pathlib import Path
1111

12-
import saneyaml
1312
from aboutcode.pipeline import LoopProgress
1413
from fetchcode.vcs import fetch_via_vcs
1514
from yaml import YAMLError
1615

1716
from vulnerabilities.models import AdvisoryAlias
18-
from vulnerabilities.models import AdvisoryDetectionRule
17+
from vulnerabilities.models import DetectionRule
18+
from vulnerabilities.models import DetectionRuleTypes
1919
from vulnerabilities.pipelines import VulnerableCodePipeline
2020
from vulnerabilities.utils import find_all_cve
2121

@@ -50,42 +50,48 @@ def collect_and_store_rules(self):
5050
self.log(f"Enhancing the vulnerability with {rules_count:,d} rule records")
5151
progress = LoopProgress(total_iterations=rules_count, logger=self.log)
5252
for file_path in progress.iter(yaml_files):
53-
cve_ids = find_all_cve(str(file_path))
54-
if not cve_ids or len(cve_ids) > 1:
53+
if any(part in [".github", "images", "documentation"] for part in file_path.parts):
5554
continue
5655

57-
cve_id = cve_ids[0]
58-
5956
with open(file_path, "r") as f:
6057
try:
61-
rule_data = saneyaml.load(f)
58+
rule_data = f.read()
6259
except YAMLError as err:
6360
self.log(f"Invalid YAML in {file_path}: {err}. Skipping.")
6461
continue
6562

66-
advisories = set()
67-
try:
68-
if alias := AdvisoryAlias.objects.get(alias=cve_id):
69-
for adv in alias.advisories.all():
70-
advisories.add(adv)
71-
except AdvisoryAlias.DoesNotExist:
72-
self.log(f"Advisory {file_path.name} not found.")
73-
continue
74-
75-
rule_text = saneyaml.dump(rule_data)
7663
rule_url = f"https://raw.githubusercontent.com/SigmaHQ/sigma/refs/heads/master/{file_path.relative_to(base_directory)}"
64+
cve_ids = find_all_cve(str(file_path))
65+
found_advisories = set()
66+
for cve_id in cve_ids:
67+
try:
68+
alias = AdvisoryAlias.objects.get(alias=cve_id)
69+
for adv in alias.advisories.all():
70+
found_advisories.add(adv)
71+
except AdvisoryAlias.DoesNotExist:
72+
self.log(f"Advisory {file_path.name} not found.")
73+
continue
7774

78-
for advisory in advisories:
79-
AdvisoryDetectionRule.objects.update_or_create(
80-
advisory=advisory,
81-
rule_type="sigma",
75+
for adv in found_advisories:
76+
DetectionRule.objects.update_or_create(
77+
rule_text=rule_data,
78+
advisory=adv,
8279
defaults={
83-
"rule_text": rule_text,
80+
"rule_type": DetectionRuleTypes.SIGMA,
8481
"source_url": rule_url,
8582
},
8683
)
8784

88-
self.log(f"Successfully added {rules_count:,d} rules advisory")
85+
if not found_advisories:
86+
DetectionRule.objects.update_or_create(
87+
rule_text=rule_data,
88+
advisory=None,
89+
defaults={
90+
"rule_type": DetectionRuleTypes.SIGMA,
91+
"source_url": rule_url,
92+
},
93+
)
94+
self.log(f"Successfully processed rules.")
8995

9096
def clean_downloads(self):
9197
if self.vcs_response:

vulnerabilities/tests/pipelines/v2_improvers/test_sigma_rules.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
import pytest
1616

1717
from vulnerabilities.models import AdvisoryAlias
18-
from vulnerabilities.models import AdvisoryDetectionRule
1918
from vulnerabilities.models import AdvisoryV2
19+
from vulnerabilities.models import DetectionRule
2020
from vulnerabilities.pipelines.v2_improvers.sigma_rules import SigmaRulesImproverPipeline
2121

2222
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -67,6 +67,6 @@ def test_sigma_rules_db_improver(mock_fetch_via_vcs):
6767
improver = SigmaRulesImproverPipeline()
6868
improver.execute()
6969

70-
assert len(AdvisoryDetectionRule.objects.all()) == 3
71-
sigma_rule = AdvisoryDetectionRule.objects.first()
70+
assert len(DetectionRule.objects.all()) == 3
71+
sigma_rule = DetectionRule.objects.first()
7272
assert sigma_rule.rule_type == "sigma"

0 commit comments

Comments
 (0)