Skip to content

Commit af3e53d

Browse files
committed
feat: implement lazy loading and improve error handling for yara dependency
- Implement lazy loading of yara module with proper type hints - Add error messages when yara is not available - Add helper function _check_yara_available() for consistent error handling
1 parent 67b8b7a commit af3e53d

File tree

1 file changed

+35
-9
lines changed
  • nemoguardrails/library/injection_detection

1 file changed

+35
-9
lines changed

nemoguardrails/library/injection_detection/actions.py

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
from pathlib import Path
3535
from typing import Tuple, Union
3636

37-
import yara
37+
yara = None
38+
try:
39+
import yara
40+
except ImportError:
41+
pass
3842

3943
from nemoguardrails import RailsConfig
4044
from nemoguardrails.actions import action
@@ -45,6 +49,14 @@
4549
log = 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+
4860
def _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,15 @@ 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+
_check_yara_available()
149+
135150
if len(rule_names) == 0:
136151
log.warning(
137152
"Injection config was provided but no modules were specified. Returning None."
@@ -150,7 +165,7 @@ def load_rules(yara_path: Path, rule_names: Tuple) -> Union[yara.Rules, None]:
150165
return rules
151166

152167

153-
def omit_injection(text: str, matches: list[yara.Match]) -> str:
168+
def omit_injection(text: str, matches: list["yara.Match"]) -> str:
154169
"""
155170
Attempts to strip the offending injection attempts from the provided text.
156171
@@ -160,11 +175,16 @@ def omit_injection(text: str, matches: list[yara.Match]) -> str:
160175
161176
Args:
162177
text (str): The text to check for command injection.
163-
matches (list[yara.Match]): A list of YARA rule matches.
178+
matches (list['yara.Match']): A list of YARA rule matches.
164179
165180
Returns:
166181
str: The text with the detected injections stripped out.
182+
183+
Raises:
184+
ImportError: If the yara module is not installed.
167185
"""
186+
_check_yara_available()
187+
168188
# Copy the text to a placeholder variable
169189
modified_text = text
170190
for match in matches:
@@ -180,7 +200,7 @@ def omit_injection(text: str, matches: list[yara.Match]) -> str:
180200
return modified_text
181201

182202

183-
def sanitize_injection(text: str, matches: list[yara.Match]) -> str:
203+
def sanitize_injection(text: str, matches: list["yara.Match"]) -> str:
184204
"""
185205
Attempts to sanitize the offending injection attempts in the provided text.
186206
This is done by 'de-fanging' the offending content, transforming it into a state that will not execute
@@ -193,20 +213,23 @@ def sanitize_injection(text: str, matches: list[yara.Match]) -> str:
193213
194214
Args:
195215
text (str): The text to check for command injection.
196-
matches (list[yara.Match]): A list of YARA rule matches.
216+
matches (list['yara.Match']): A list of YARA rule matches.
197217
198218
Returns:
199219
str: The text with the detected injections sanitized.
200220
201221
Raises:
202222
NotImplementedError: If the sanitization logic is not implemented.
223+
ImportError: If the yara module is not installed.
203224
"""
225+
_check_yara_available()
226+
204227
raise NotImplementedError(
205228
"Injection sanitization is not yet implemented. Please use 'reject' or 'omit'"
206229
)
207230

208231

209-
def reject_injection(text: str, rules: yara.Rules) -> Tuple[bool, str]:
232+
def reject_injection(text: str, rules: "yara.Rules") -> Tuple[bool, str]:
210233
"""
211234
Detects whether the provided text contains potential injection attempts.
212235
@@ -215,15 +238,18 @@ def reject_injection(text: str, rules: yara.Rules) -> Tuple[bool, str]:
215238
216239
Args:
217240
text (str): The text to check for command injection.
218-
rules (yara.Rules): The loaded YARA rules.
241+
rules ('yara.Rules'): The loaded YARA rules.
219242
220243
Returns:
221244
bool: True if attempted exploitation is detected, False otherwise.
222245
str: list of matches as a string
223246
224247
Raises:
225248
ValueError: If the `action` parameter in the configuration is invalid.
249+
ImportError: If the yara module is not installed.
226250
"""
251+
_check_yara_available()
252+
227253
if rules is None:
228254
log.warning(
229255
"reject_injection guardrail was invoked but no rules were specified in the InjectionDetection config."

0 commit comments

Comments
 (0)