Skip to content

Commit

Permalink
Initial support for vetter validation
Browse files Browse the repository at this point in the history
and uniqueness enforcement
for groups and mitre tactics.
The errors still are not
high quality and will take some
time to fix.
  • Loading branch information
pyth0n1c committed Feb 5, 2025
1 parent ff372fb commit 07a92c1
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 12 deletions.
7 changes: 5 additions & 2 deletions contentctl/objects/annotated_types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from pydantic import Field
from typing import Annotated

from pydantic import Field

CVE_TYPE = Annotated[str, Field(pattern=r"^CVE-[1|2]\d{3}-\d+$")]
MITRE_ATTACK_ID_TYPE = Annotated[str, Field(pattern=r"^T\d{4}(.\d{3})?$")]
MITRE_ATTACK_ID_TYPE_PARENT = Annotated[str, Field(pattern=r"^T\d{4}$")]
MITRE_ATTACK_ID_TYPE_SUBTYPE = Annotated[str, Field(pattern=r"^T\d{4}(.\d{3})$")]
MITRE_ATTACK_ID_TYPE = MITRE_ATTACK_ID_TYPE_PARENT | MITRE_ATTACK_ID_TYPE_SUBTYPE
APPID_TYPE = Annotated[str, Field(pattern="^[a-zA-Z0-9_-]+$")]
6 changes: 6 additions & 0 deletions contentctl/objects/detection_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@ def addAttackEnrichment(self, info: ValidationInfo):
if len(missing_tactics) > 0:
raise ValueError(f"Missing Mitre Attack IDs. {missing_tactics} not found.")
else:
try:
MitreAttackEnrichment.checkParentTypeNotDefinedWhenSubtypeDefined(
mitre_enrichments
)
except Exception as e:
raise ValueError(e)
self.mitre_attack_enrichments = mitre_enrichments

return self
Expand Down
47 changes: 42 additions & 5 deletions contentctl/objects/mitre_attack_enrichment.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,47 @@ def __hash__(self) -> int:
return id(self)

@staticmethod
def getUniqueGroups(groups: list[MitreAttackGroup]) -> list[MitreAttackGroup]:
def getUniqueGroups(
enrichments: list[MitreAttackEnrichment],
) -> list[MitreAttackGroup]:
group_set: set[MitreAttackGroup] = set()
for group in groups:
g = set(group)
group_set.update(g)

for enrichment in enrichments:
group_set.update(set(enrichment.mitre_attack_group_objects))
return sorted(group_set)

@staticmethod
def checkParentTypeNotDefinedWhenSubtypeDefined(
enrichments: list[MitreAttackEnrichment],
) -> None:
# Get all the mitre_attack_ids
mitre_attack_ids = [enrichment.mitre_attack_id for enrichment in enrichments]
# Split these into two groups - one that just has parents and one which has subtypes as well
mitre_attack_id_parents = [
mitre_attack_id
for mitre_attack_id in mitre_attack_ids
if "." not in mitre_attack_id
]
mitre_attack_id_subtypes = [
mitre_attack_id
for mitre_attack_id in mitre_attack_ids
if "." in mitre_attack_id
]

subtype_and_parent_exist_exceptions: list[Exception] = []

for mitre_attack_id in mitre_attack_id_subtypes:
parent_id = mitre_attack_id.split(".")[0]
if parent_id in mitre_attack_id_parents:
subtype_and_parent_exist_exceptions.append(
Exception(
f"Overlapping parent and subtype tactic: [Parent: '{parent_id}', Subtype: '{mitre_attack_id}']"
)
)
if len(subtype_and_parent_exist_exceptions):
raise ExceptionGroup(
"Error: both MITRE Attack ID Subtype and Parent are defined. "
"A parent tactic OR a subtype tactic may be defined, but not both",
subtype_and_parent_exist_exceptions,
)

return None
11 changes: 6 additions & 5 deletions contentctl/objects/story_tags.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
from __future__ import annotations
from pydantic import BaseModel, Field, model_serializer, ConfigDict
from typing import List, Set, Optional

from enum import Enum
from typing import List, Optional, Set

from contentctl.objects.mitre_attack_enrichment import MitreAttackEnrichment
from pydantic import BaseModel, ConfigDict, Field, model_serializer

from contentctl.objects.annotated_types import CVE_TYPE, MITRE_ATTACK_ID_TYPE
from contentctl.objects.enums import (
StoryCategory,
DataModel,
KillChainPhase,
SecurityContentProductName,
StoryCategory,
)
from contentctl.objects.annotated_types import CVE_TYPE, MITRE_ATTACK_ID_TYPE
from contentctl.objects.mitre_attack_enrichment import MitreAttackEnrichment


class StoryUseCase(str, Enum):
Expand Down

0 comments on commit 07a92c1

Please sign in to comment.