1- from typing import Any , ClassVar , Literal
1+ import logging
2+ from typing import ClassVar , Literal
23
34import plyara
45import plyara .exceptions
1920}
2021
2122
23+ logger = logging .getLogger (__name__ )
24+
25+
2226class MatchInstance (BaseModel ):
2327 """
2428 Represents an instance of a string match.
@@ -122,10 +126,8 @@ class Yara(indicator.Indicator):
122126 dependencies : list [str ] = []
123127 private : bool = False
124128
125- @model_validator (mode = "before" )
126- @classmethod
127- def validate_yara (cls , data : Any ):
128- rule = data .get ("pattern" )
129+ def validate_yara (self ):
130+ rule = self .pattern
129131 if not rule :
130132 raise ValueError ("Yara rule body is required." )
131133 try :
@@ -138,13 +140,16 @@ def validate_yara(cls, data: Any):
138140 raise ValueError ("No valid Yara rules found in the rule body." )
139141 parsed_rule = rules [0 ]
140142 rule_deps = set (plyara .utils .detect_dependencies (parsed_rule ))
141- data ["dependencies" ] = rule_deps - ALLOWED_EXTERNALS .keys ()
142- data ["name" ] = parsed_rule ["rule_name" ]
143- data ["private" ] = "private" in parsed_rule .get ("scopes" , [])
144-
145- return data
143+ self .dependencies = list (rule_deps - ALLOWED_EXTERNALS .keys ())
144+ self .name = parsed_rule ["rule_name" ]
145+ self .private = "private" in parsed_rule .get ("scopes" , [])
146146
147147 def save (self ):
148+ try :
149+ self .validate_yara ()
150+ except ValueError as error :
151+ raise errors .ObjectCreationError (str (error )) from error
152+
148153 missing_deps = []
149154 for dep_name in self .dependencies :
150155 dep = Yara .find (name = dep_name )
@@ -247,19 +252,36 @@ def rule_with_dependencies(
247252
248253 concatenated_rules = ""
249254
250- parsed_rule = plyara .Plyara ().parse_string (self .pattern )[0 ]
251- dependencies = plyara .utils .detect_dependencies (parsed_rule )
255+ dependencies = self .dependencies
252256
253257 for dependency in dependencies :
258+ if dependency in resolved :
259+ continue
260+ logger .info (f"Resolving dependency: { dependency } " )
254261 dep_rule = Yara .find (name = dependency )
255262 if not dep_rule :
256263 raise ValueError (f"Rule depends on unknown dependency '{ dependency } '" )
257- if dep_rule .name not in resolved :
258- concatenated_rules += dep_rule .rule_with_dependencies (resolved , seen )
264+ concatenated_rules += dep_rule .rule_with_dependencies (resolved , seen )
259265
260266 if self .name not in resolved :
261267 concatenated_rules += self .pattern + "\n \n "
262268 resolved .add (self .name )
263269
264270 seen .remove (self .name )
265271 return concatenated_rules
272+
273+ @classmethod
274+ def generate_yara_bundle (cls , rules : list ["Yara" ]) -> str :
275+ """Export a list of Yara rules to a single string.
276+
277+ Args:
278+ rules: A list of Yara rules to export.
279+
280+ Returns:
281+ A string containing the exported rules.
282+ """
283+ resolved : set [str ] = set ()
284+ bulk_rules = ""
285+ for rule in rules :
286+ bulk_rules += rule .rule_with_dependencies (resolved )
287+ return bulk_rules
0 commit comments