Merge PR #4551 from @frack113 - chore: move more tests to pySigma

chore: Add attacktag and tlptag to pySigma tests
---------

Co-authored-by: Nasreddine Bencherchali <8741929+nasbench@users.noreply.github.com>
This commit is contained in:
frack113
2023-11-15 16:40:33 +01:00
committed by GitHub
parent 0f5f989604
commit d577872761
5 changed files with 161 additions and 150 deletions
+1 -1
View File
@@ -79,7 +79,7 @@ jobs:
sigma check --fail-on-error --fail-on-issues --validation-config tests/sigma_cli_conf.yml rules*
- name: Test Sigma Rules
run: |
pip install PyYAML attackcti colorama
pip install PyYAML colorama
python tests/test_rules.py
check-baseline-win7:
-3
View File
@@ -1,3 +0,0 @@
[submodule "tests/cti"]
path = tests/cti
url = https://github.com/mitre/cti.git
Submodule tests/cti deleted from 340ee45256
+2
View File
@@ -1,9 +1,11 @@
validators:
- attacktag
- all_of_them_condition
- duplicate_tag
- duplicate_title
- identifier_existence
- identifier_uniqueness
- tlptag
exclusions:
# escaped_wildcard
021310d9-30a6-480a-84b7-eaa69aeb92bb: escaped_wildcard
+158 -145
View File
@@ -11,41 +11,52 @@ import unittest
import yaml
import re
import string
from attackcti import attack_client
#from attackcti import attack_client
from colorama import init
from colorama import Fore
import collections
class TestRules(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("Calling get_mitre_data()")
# Get Current Data from MITRE ATT&CK®
cls.MITRE_ALL = get_mitre_data()
print("Catched data - starting tests...")
# Old Tests cover by pySigma 0.10.6 and simgma-cli 0.7.8
# Use sigma check --fail-on-error --fail-on-issues --validation-config tests/sigma_cli_conf.yml rules*
#
# def test_duplicate_tags(self): sigma-cli validators duplicate_tag
# def test_all_of_them_condition(self): sigma-cli validator all_of_them_condition
# def test_missing_id(self): sigma-cli error & validator identifier_existence identifier_uniqueness
# def test_duplicate_titles(self): sigma-cli validators duplicate_title
# def test_unknown_value_modifier(self): sigma-cli error & validator SigmaModifierError
# def test_confirm_correct_mitre_tags(self): sigma-cli validators attacktag
# def test_optional_tlp(self): sigma-cli validators tlptag
MITRE_TECHNIQUE_NAMES = [
"process_injection",
"signed_binary_proxy_execution",
"process_injection",
] # incomplete list
MITRE_TACTICS = [
"initial_access",
"execution",
"persistence",
"privilege_escalation",
"defense_evasion",
"credential_access",
"discovery",
"lateral_movement",
"collection",
"exfiltration",
"command_and_control",
"impact",
"launch",
]
# Don't use trademarks in rules - they require non-ASCII characters to be used on we don't want them in our rules
class TestRules(unittest.TestCase):
# @classmethod
# def setUpClass(cls):
# print("Calling get_mitre_data()")
# # Get Current Data from MITRE ATT&CK®
# cls.MITRE_ALL = get_mitre_data()
# print("Catched data - starting tests...")
# MITRE_TECHNIQUE_NAMES = [
# "process_injection",
# "signed_binary_proxy_execution",
# "process_injection",
# ] # incomplete list
# MITRE_TACTICS = [
# "initial_access",
# "execution",
# "persistence",
# "privilege_escalation",
# "defense_evasion",
# "credential_access",
# "discovery",
# "lateral_movement",
# "collection",
# "exfiltration",
# "command_and_control",
# "impact",
# "launch",
# ]
# # Don't use trademarks in rules - they require non-ASCII characters to be used on we don't want them in our rules
TRADE_MARKS = {"MITRE ATT&CK", "ATT&CK"}
path_to_rules = [
@@ -137,28 +148,29 @@ class TestRules(unittest.TestCase):
+ "There are rules with incorrect/unknown Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://github.com/SigmaHQ/sigma-specification/blob/main/Tags_specification.md ",
)
def test_confirm_correct_mitre_tags(self):
files_with_incorrect_mitre_tags = []
# sigma-cli validators attacktag
# def test_confirm_correct_mitre_tags(self):
# files_with_incorrect_mitre_tags = []
for file in self.yield_next_rule_file_path(self.path_to_rules):
tags = self.get_rule_part(file_path=file, part_name="tags")
if tags:
for tag in tags:
if tag.startswith("attack.") and tag not in self.MITRE_ALL:
print(
Fore.RED
+ "Rule {} has the following incorrect MITRE tag {}".format(
file, tag
)
)
files_with_incorrect_mitre_tags.append(file)
# for file in self.yield_next_rule_file_path(self.path_to_rules):
# tags = self.get_rule_part(file_path=file, part_name="tags")
# if tags:
# for tag in tags:
# if tag.startswith("attack.") and tag not in self.MITRE_ALL:
# print(
# Fore.RED
# + "Rule {} has the following incorrect MITRE tag {}".format(
# file, tag
# )
# )
# files_with_incorrect_mitre_tags.append(file)
self.assertEqual(
files_with_incorrect_mitre_tags,
[],
Fore.RED
+ "There are rules with incorrect/unknown MITRE Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://attack.mitre.org/ ",
)
# self.assertEqual(
# files_with_incorrect_mitre_tags,
# [],
# Fore.RED
# + "There are rules with incorrect/unknown MITRE Tags. (please inform us about new tags that are not yet supported in our tests) and check the correct tags here: https://attack.mitre.org/ ",
# )
# sigma validators duplicate_tag
# def test_duplicate_tags(self):
@@ -933,37 +945,38 @@ class TestRules(unittest.TestCase):
+ "There are rules with malformed 'license' fields. (has to be a string )",
)
def test_optional_tlp(self):
faulty_rules = []
valid_tlp = [
"WHITE",
"GREEN",
"AMBER",
"RED",
]
for file in self.yield_next_rule_file_path(self.path_to_rules):
tlp_str = self.get_rule_part(file_path=file, part_name="tlp")
if tlp_str:
# it exists but isn't a string
if not isinstance(tlp_str, str):
print(
Fore.YELLOW
+ "Rule {} has a 'tlp' field that isn't a string.".format(file)
)
faulty_rules.append(file)
elif not tlp_str.upper() in valid_tlp:
print(
Fore.YELLOW
+ "Rule {} has a 'tlp' field with not valid value.".format(file)
)
faulty_rules.append(file)
# sigma-cli validators tlptag
# def test_optional_tlp(self):
# faulty_rules = []
# valid_tlp = [
# "WHITE",
# "GREEN",
# "AMBER",
# "RED",
# ]
# for file in self.yield_next_rule_file_path(self.path_to_rules):
# tlp_str = self.get_rule_part(file_path=file, part_name="tlp")
# if tlp_str:
# # it exists but isn't a string
# if not isinstance(tlp_str, str):
# print(
# Fore.YELLOW
# + "Rule {} has a 'tlp' field that isn't a string.".format(file)
# )
# faulty_rules.append(file)
# elif not tlp_str.upper() in valid_tlp:
# print(
# Fore.YELLOW
# + "Rule {} has a 'tlp' field with not valid value.".format(file)
# )
# faulty_rules.append(file)
self.assertEqual(
faulty_rules,
[],
Fore.RED
+ "There are rules with malformed optional 'tlp' fields. (https://www.cisa.gov/tlp)",
)
# self.assertEqual(
# faulty_rules,
# [],
# Fore.RED
# + "There are rules with malformed optional 'tlp' fields. (https://www.cisa.gov/tlp)",
# )
def test_optional_target(self):
faulty_rules = []
@@ -1602,7 +1615,7 @@ class TestRules(unittest.TestCase):
# self.assertEqual(faulty_rules, [], Fore.RED + "There are rules with common typos in field names.")
# Sigma error validator SigmaModifierError
# Sigma error SigmaModifierError
# def test_unknown_value_modifier(self):
# known_modifiers = [
# "contains",
@@ -1909,74 +1922,74 @@ class TestRules(unittest.TestCase):
faulty_rules, [], Fore.RED + "There are rules using illegal re-escapes"
)
# sigma-cli validators attacktag
# def get_mitre_data():
# """
# Use Tags from CTI subrepo to get consitant data
# """
# cti_path = "cti/"
# cti_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), cti_path)
def get_mitre_data():
"""
Use Tags from CTI subrepo to get consitant data
"""
cti_path = "cti/"
cti_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), cti_path)
# # Get ATT&CK information
# lift = attack_client(local_path=cti_path)
# # Techniques
# MITRE_TECHNIQUES = []
# MITRE_TECHNIQUE_NAMES = []
# MITRE_PHASE_NAMES = set()
# MITRE_TOOLS = []
# MITRE_GROUPS = []
# # Techniques
# enterprise_techniques = lift.get_enterprise_techniques()
# for t in enterprise_techniques:
# MITRE_TECHNIQUE_NAMES.append(
# t["name"].lower().replace(" ", "_").replace("-", "_")
# )
# for r in t.external_references:
# if "external_id" in r:
# MITRE_TECHNIQUES.append(r["external_id"].lower())
# if "kill_chain_phases" in t:
# for kc in t["kill_chain_phases"]:
# if "phase_name" in kc:
# MITRE_PHASE_NAMES.add(kc["phase_name"].replace("-", "_"))
# # Tools / Malware
# enterprise_tools = lift.get_enterprise_tools()
# for t in enterprise_tools:
# for r in t.external_references:
# if "external_id" in r:
# MITRE_TOOLS.append(r["external_id"].lower())
# enterprise_malware = lift.get_enterprise_malware()
# for m in enterprise_malware:
# for r in m.external_references:
# if "external_id" in r:
# MITRE_TOOLS.append(r["external_id"].lower())
# # Groups
# enterprise_groups = lift.get_enterprise_groups()
# for g in enterprise_groups:
# for r in g.external_references:
# if "external_id" in r:
# MITRE_GROUPS.append(r["external_id"].lower())
# Get ATT&CK information
lift = attack_client(local_path=cti_path)
# Techniques
MITRE_TECHNIQUES = []
MITRE_TECHNIQUE_NAMES = []
MITRE_PHASE_NAMES = set()
MITRE_TOOLS = []
MITRE_GROUPS = []
# Techniques
enterprise_techniques = lift.get_enterprise_techniques()
for t in enterprise_techniques:
MITRE_TECHNIQUE_NAMES.append(
t["name"].lower().replace(" ", "_").replace("-", "_")
)
for r in t.external_references:
if "external_id" in r:
MITRE_TECHNIQUES.append(r["external_id"].lower())
if "kill_chain_phases" in t:
for kc in t["kill_chain_phases"]:
if "phase_name" in kc:
MITRE_PHASE_NAMES.add(kc["phase_name"].replace("-", "_"))
# Tools / Malware
enterprise_tools = lift.get_enterprise_tools()
for t in enterprise_tools:
for r in t.external_references:
if "external_id" in r:
MITRE_TOOLS.append(r["external_id"].lower())
enterprise_malware = lift.get_enterprise_malware()
for m in enterprise_malware:
for r in m.external_references:
if "external_id" in r:
MITRE_TOOLS.append(r["external_id"].lower())
# Groups
enterprise_groups = lift.get_enterprise_groups()
for g in enterprise_groups:
for r in g.external_references:
if "external_id" in r:
MITRE_GROUPS.append(r["external_id"].lower())
# # Debugging
# print(
# "MITRE ATT&CK LIST LENGTHS: %d %d %d %d %d"
# % (
# len(MITRE_TECHNIQUES),
# len(MITRE_TECHNIQUE_NAMES),
# len(list(MITRE_PHASE_NAMES)),
# len(MITRE_GROUPS),
# len(MITRE_TOOLS),
# )
# )
# Debugging
print(
"MITRE ATT&CK LIST LENGTHS: %d %d %d %d %d"
% (
len(MITRE_TECHNIQUES),
len(MITRE_TECHNIQUE_NAMES),
len(list(MITRE_PHASE_NAMES)),
len(MITRE_GROUPS),
len(MITRE_TOOLS),
)
)
# Combine all IDs to a big tag list
return [
"attack." + item
for item in MITRE_TECHNIQUES
+ MITRE_TECHNIQUE_NAMES
+ list(MITRE_PHASE_NAMES)
+ MITRE_GROUPS
+ MITRE_TOOLS
]
# # Combine all IDs to a big tag list
# return [
# "attack." + item
# for item in MITRE_TECHNIQUES
# + MITRE_TECHNIQUE_NAMES
# + list(MITRE_PHASE_NAMES)
# + MITRE_GROUPS
# + MITRE_TOOLS
# ]
if __name__ == "__main__":