3434from pathlib import Path
3535from typing import Tuple , Union
3636
37- import yara
37+ yara = None
38+ try :
39+ import yara
40+ except ImportError :
41+ pass
3842
3943from nemoguardrails import RailsConfig
4044from nemoguardrails .actions import action
4549log = logging .getLogger (__name__ )
4650
4751
52+ def _check_yara_available ():
53+ if yara is None :
54+ raise ImportError (
55+ "The yara module is required for injection detection. "
56+ "Please install it using: pip install yara-python"
57+ )
58+
59+
4860def _validate_unpack_config (config : RailsConfig ) -> Tuple [str , Path , Tuple [str ]]:
4961 """
5062 Validates and unpacks the injection detection configuration.
@@ -117,7 +129,7 @@ def _validate_unpack_config(config: RailsConfig) -> Tuple[str, Path, Tuple[str]]
117129
118130
119131@lru_cache ()
120- def load_rules (yara_path : Path , rule_names : Tuple ) -> Union [yara .Rules , None ]:
132+ def _load_rules (yara_path : Path , rule_names : Tuple ) -> Union [" yara.Rules" , None ]:
121133 """
122134 Loads and compiles YARA rules from the specified path and rule names.
123135
@@ -126,12 +138,14 @@ def load_rules(yara_path: Path, rule_names: Tuple) -> Union[yara.Rules, None]:
126138 rule_names (Tuple): A tuple of YARA rule names to load.
127139
128140 Returns:
129- Union[yara.Rules, None]: The compiled YARA rules object if successful,
141+ Union[' yara.Rules' , None]: The compiled YARA rules object if successful,
130142 or None if no rule names are provided.
131143
132144 Raises:
133145 yara.SyntaxError: If there is a syntax error in the YARA rules.
146+ ImportError: If the yara module is not installed.
134147 """
148+
135149 if len (rule_names ) == 0 :
136150 log .warning (
137151 "Injection config was provided but no modules were specified. Returning None."
@@ -150,7 +164,7 @@ def load_rules(yara_path: Path, rule_names: Tuple) -> Union[yara.Rules, None]:
150164 return rules
151165
152166
153- def omit_injection (text : str , matches : list [yara .Match ]) -> str :
167+ def _omit_injection (text : str , matches : list [" yara.Match" ]) -> str :
154168 """
155169 Attempts to strip the offending injection attempts from the provided text.
156170
@@ -160,11 +174,15 @@ def omit_injection(text: str, matches: list[yara.Match]) -> str:
160174
161175 Args:
162176 text (str): The text to check for command injection.
163- matches (list[yara.Match]): A list of YARA rule matches.
177+ matches (list[' yara.Match' ]): A list of YARA rule matches.
164178
165179 Returns:
166180 str: The text with the detected injections stripped out.
181+
182+ Raises:
183+ ImportError: If the yara module is not installed.
167184 """
185+
168186 # Copy the text to a placeholder variable
169187 modified_text = text
170188 for match in matches :
@@ -180,7 +198,7 @@ def omit_injection(text: str, matches: list[yara.Match]) -> str:
180198 return modified_text
181199
182200
183- def sanitize_injection (text : str , matches : list [yara .Match ]) -> str :
201+ def _sanitize_injection (text : str , matches : list [" yara.Match" ]) -> str :
184202 """
185203 Attempts to sanitize the offending injection attempts in the provided text.
186204 This is done by 'de-fanging' the offending content, transforming it into a state that will not execute
@@ -193,20 +211,22 @@ def sanitize_injection(text: str, matches: list[yara.Match]) -> str:
193211
194212 Args:
195213 text (str): The text to check for command injection.
196- matches (list[yara.Match]): A list of YARA rule matches.
214+ matches (list[' yara.Match' ]): A list of YARA rule matches.
197215
198216 Returns:
199217 str: The text with the detected injections sanitized.
200218
201219 Raises:
202220 NotImplementedError: If the sanitization logic is not implemented.
221+ ImportError: If the yara module is not installed.
203222 """
223+
204224 raise NotImplementedError (
205225 "Injection sanitization is not yet implemented. Please use 'reject' or 'omit'"
206226 )
207227
208228
209- def reject_injection (text : str , rules : yara .Rules ) -> Tuple [bool , str ]:
229+ def _reject_injection (text : str , rules : " yara.Rules" ) -> Tuple [bool , str ]:
210230 """
211231 Detects whether the provided text contains potential injection attempts.
212232
@@ -215,15 +235,17 @@ def reject_injection(text: str, rules: yara.Rules) -> Tuple[bool, str]:
215235
216236 Args:
217237 text (str): The text to check for command injection.
218- rules (yara.Rules): The loaded YARA rules.
238+ rules (' yara.Rules' ): The loaded YARA rules.
219239
220240 Returns:
221241 bool: True if attempted exploitation is detected, False otherwise.
222242 str: list of matches as a string
223243
224244 Raises:
225245 ValueError: If the `action` parameter in the configuration is invalid.
246+ ImportError: If the yara module is not installed.
226247 """
248+
227249 if rules is None :
228250 log .warning (
229251 "reject_injection guardrail was invoked but no rules were specified in the InjectionDetection config."
@@ -258,10 +280,12 @@ async def injection_detection(text: str, config: RailsConfig) -> str:
258280 ValueError: If the `action` parameter in the configuration is invalid.
259281 NotImplementedError: If an unsupported action is encountered.
260282 """
283+ _check_yara_available ()
284+
261285 action_option , yara_path , rule_names = _validate_unpack_config (config )
262- rules = load_rules (yara_path , rule_names )
286+ rules = _load_rules (yara_path , rule_names )
263287 if action_option == "reject" :
264- verdict , detections = reject_injection (text , rules )
288+ verdict , detections = _reject_injection (text , rules )
265289 if verdict :
266290 return f"I'm sorry, the desired output triggered rule(s) designed to mitigate exploitation of { detections } ."
267291 else :
@@ -276,9 +300,9 @@ async def injection_detection(text: str, config: RailsConfig) -> str:
276300 matches_string = ", " .join ([match_name .rule for match_name in matches ])
277301 log .info (f"Input matched on rule { matches_string } ." )
278302 if action_option == "omit" :
279- return omit_injection (text , matches )
303+ return _omit_injection (text , matches )
280304 elif action_option == "sanitize" :
281- return sanitize_injection (text , matches )
305+ return _sanitize_injection (text , matches )
282306 else :
283307 # We should never ever hit this since we inspect the action option above, but putting an error here anyway.
284308 raise NotImplementedError (
0 commit comments