Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions nemoguardrails/library/injection_detection/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,25 @@ def _load_rules(
log.warning("Injection config was provided but no modules were specified. Returning None.")
return None

if yara_rules:
# When inline `yara_rules` are provided, only those rules are available
# (built-in rules are not loaded in this mode). Every requested rule name
# in `injections` must therefore be present in `yara_rules`; otherwise we
# fail with a clear, actionable error instead of an opaque KeyError.
missing_rules = [name for name in rule_names if name not in yara_rules]
if missing_rules:
msg = (
"Provided set of `injections` in injection config contains rule names "
"not present in the inline `yara_rules`: %r. Available inline rules are %r. "
"Note that built-in rules are not loaded when `yara_rules` is provided."
) % (missing_rules, sorted(yara_rules))
log.error(msg)
raise ValueError(msg)

try:
if yara_rules:
rules_source = {name: rule for name, rule in yara_rules.items() if name in rule_names}
rules = yara.compile(sources={rule_name: rules_source[rule_name] for rule_name in rule_names})
rules_source = {rule_name: yara_rules[rule_name] for rule_name in rule_names}
rules = yara.compile(sources=rules_source)
else:
rules_to_load = {rule_name: str(yara_path.joinpath(f"{rule_name}.yara")) for rule_name in rule_names}
rules = yara.compile(filepaths=rules_to_load)
Expand Down
42 changes: 42 additions & 0 deletions tests/test_injection_detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,48 @@ def test_load_inline_yara_rules():
assert matches[0].namespace == inline_rule_name


def test_inline_yara_rules_missing_injection_name_raises_value_error():
"""Requested `injections` names absent from inline `yara_rules` must raise a
clear ValueError instead of an opaque KeyError.

Regression test: `_load_rules` previously indexed the inline rules dict with
every name in `injections`, so listing a rule name that was not defined in
`yara_rules` (e.g. mixing a built-in rule name with an inline custom rule)
crashed with `KeyError` inside the YARA compile call, and neither
`_validate_injection_config` nor `_extract_injection_config` caught it.
"""
inline_rule_name = "custom_rule"
inline_rule_content = 'rule custom_rule { strings: $a = "foo" condition: $a }'

config = RailsConfig.from_content(
yaml_content=f"""
models: []
rails:
config:
injection_detection:
injections:
- sqli
- {inline_rule_name}
action:
reject
yara_rules:
{inline_rule_name}: |-
{inline_rule_content}
""",
colang_content="",
)

_validate_injection_config(config)
action_option, yara_path, rule_names, yara_rules = _extract_injection_config(config)

# "sqli" is requested via `injections` but is not defined in `yara_rules`.
assert "sqli" in rule_names
assert "sqli" not in yara_rules

with pytest.raises(ValueError, match="sqli"):
_load_rules(yara_path, rule_names, yara_rules)


@pytest.mark.asyncio
async def test_omit_injection_action():
"""Test the omit action for injection detection."""
Expand Down