nova-hunting 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nova_hunting-0.1.0/LICENCE +21 -0
- nova_hunting-0.1.0/PKG-INFO +99 -0
- nova_hunting-0.1.0/README.md +63 -0
- nova_hunting-0.1.0/nova/__init__.py +28 -0
- nova_hunting-0.1.0/nova/core/__init__.py +29 -0
- nova_hunting-0.1.0/nova/core/matcher.py +198 -0
- nova_hunting-0.1.0/nova/core/parser.py +1006 -0
- nova_hunting-0.1.0/nova/core/rules.py +73 -0
- nova_hunting-0.1.0/nova/core/scanner.py +131 -0
- nova_hunting-0.1.0/nova/evaluators/__init__.py +25 -0
- nova_hunting-0.1.0/nova/evaluators/base.py +57 -0
- nova_hunting-0.1.0/nova/evaluators/condition.py +335 -0
- nova_hunting-0.1.0/nova/evaluators/keywords.py +83 -0
- nova_hunting-0.1.0/nova/evaluators/llm.py +759 -0
- nova_hunting-0.1.0/nova/evaluators/semantics.py +94 -0
- nova_hunting-0.1.0/nova/novarun.py +555 -0
- nova_hunting-0.1.0/nova/utils/__init__.py +15 -0
- nova_hunting-0.1.0/nova/utils/config.py +235 -0
- nova_hunting-0.1.0/nova/utils/helpers.py +0 -0
- nova_hunting-0.1.0/nova_hunting.egg-info/PKG-INFO +99 -0
- nova_hunting-0.1.0/nova_hunting.egg-info/SOURCES.txt +27 -0
- nova_hunting-0.1.0/nova_hunting.egg-info/dependency_links.txt +1 -0
- nova_hunting-0.1.0/nova_hunting.egg-info/entry_points.txt +2 -0
- nova_hunting-0.1.0/nova_hunting.egg-info/requires.txt +11 -0
- nova_hunting-0.1.0/nova_hunting.egg-info/top_level.txt +1 -0
- nova_hunting-0.1.0/pyproject.toml +3 -0
- nova_hunting-0.1.0/setup.cfg +4 -0
- nova_hunting-0.1.0/setup.py +28 -0
- nova_hunting-0.1.0/tests/testerror.py +677 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Thomas Roccia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: nova-hunting
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Prompt Pattern Matching Framework for Generative AI
|
|
5
|
+
Home-page: https://github.com/fr0gger/nova-framework
|
|
6
|
+
Author: Thomas Roccia
|
|
7
|
+
Author-email: contact@securitybreak.io
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENCE
|
|
15
|
+
Requires-Dist: mkdocs-material
|
|
16
|
+
Requires-Dist: mkdocs
|
|
17
|
+
Requires-Dist: mkdocs-material[imaging]
|
|
18
|
+
Requires-Dist: sentence-transformers
|
|
19
|
+
Requires-Dist: transformers
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: pyyaml
|
|
22
|
+
Requires-Dist: colorama
|
|
23
|
+
Requires-Dist: openai
|
|
24
|
+
Requires-Dist: anthropic
|
|
25
|
+
Requires-Dist: pytest
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: license
|
|
33
|
+
Dynamic: requires-dist
|
|
34
|
+
Dynamic: requires-python
|
|
35
|
+
Dynamic: summary
|
|
36
|
+
|
|
37
|
+
# NOVA: The Prompt Pattern Matching
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<img src="nova_doc/docs/nova.svg" alt="NOVA Logo">
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
Generative AI systems are rapidly being adopted and deployed across organizations. While they enhance productivity and efficiency, they also expand the attack surface.
|
|
46
|
+
|
|
47
|
+
How do you detect abusive usage of your system? How do you hunt for malicious prompts? Whether it is identifying jailbreaking attempts, preventing reputational damage, or spotting unexpected behaviors, tracking prompt TTPs can be very useful to track the usage of your AI systems.
|
|
48
|
+
|
|
49
|
+
That's where NOVA comes in!
|
|
50
|
+
|
|
51
|
+
🚧 **Disclaimer:** NOVA is currently in beta. Expect potential bugs, incomplete features, and ongoing improvements. If you identify a bug, please [report it here](https://github.com/fr0gger/nova-framework/issues).
|
|
52
|
+
|
|
53
|
+
NOVA is an open-source prompt pattern matching system combining keyword detection, semantic similarity, and LLM-based evaluation to analyze and detect prompt content.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- 🔍 **Keyword Detection:** Flag suspicious prompts using predefined keywords or regex.
|
|
58
|
+
- 💬 **Semantic Similarity:** Identify pattern variations using configurable thresholds.
|
|
59
|
+
- ✨ **LLM Matching:** Create matching rules using natural language evaluated by LLM.
|
|
60
|
+
|
|
61
|
+
Inspired by YARA syntax, NOVA rules are readable and flexible, ideal for prompt hunting and threat detection.
|
|
62
|
+
|
|
63
|
+
## Anatomy of a NOVA Rule
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
rule RuleName
|
|
67
|
+
{
|
|
68
|
+
meta:
|
|
69
|
+
description = "Rule description"
|
|
70
|
+
author = "Author name"
|
|
71
|
+
|
|
72
|
+
keywords:
|
|
73
|
+
$keyword1 = "exact text"
|
|
74
|
+
$keyword2 = /regex pattern/i
|
|
75
|
+
|
|
76
|
+
semantics:
|
|
77
|
+
$semantic1 = "semantic pattern" (0.6)
|
|
78
|
+
|
|
79
|
+
llm:
|
|
80
|
+
$llm_check = "LLM evaluation prompt" (0.7)
|
|
81
|
+
|
|
82
|
+
condition:
|
|
83
|
+
keywords.$keyword1 or semantics.$semantic1 or llm.$llm_check
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Getting Started
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install nova-framework
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
96
|
+
|
|
97
|
+
## Credits
|
|
98
|
+
|
|
99
|
+
Created and maintained by [fr0gger](https://github.com/fr0gger).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# NOVA: The Prompt Pattern Matching
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="nova_doc/docs/nova.svg" alt="NOVA Logo">
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
Generative AI systems are rapidly being adopted and deployed across organizations. While they enhance productivity and efficiency, they also expand the attack surface.
|
|
10
|
+
|
|
11
|
+
How do you detect abusive usage of your system? How do you hunt for malicious prompts? Whether it is identifying jailbreaking attempts, preventing reputational damage, or spotting unexpected behaviors, tracking prompt TTPs can be very useful to track the usage of your AI systems.
|
|
12
|
+
|
|
13
|
+
That's where NOVA comes in!
|
|
14
|
+
|
|
15
|
+
🚧 **Disclaimer:** NOVA is currently in beta. Expect potential bugs, incomplete features, and ongoing improvements. If you identify a bug, please [report it here](https://github.com/fr0gger/nova-framework/issues).
|
|
16
|
+
|
|
17
|
+
NOVA is an open-source prompt pattern matching system combining keyword detection, semantic similarity, and LLM-based evaluation to analyze and detect prompt content.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- 🔍 **Keyword Detection:** Flag suspicious prompts using predefined keywords or regex.
|
|
22
|
+
- 💬 **Semantic Similarity:** Identify pattern variations using configurable thresholds.
|
|
23
|
+
- ✨ **LLM Matching:** Create matching rules using natural language evaluated by LLM.
|
|
24
|
+
|
|
25
|
+
Inspired by YARA syntax, NOVA rules are readable and flexible, ideal for prompt hunting and threat detection.
|
|
26
|
+
|
|
27
|
+
## Anatomy of a NOVA Rule
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
rule RuleName
|
|
31
|
+
{
|
|
32
|
+
meta:
|
|
33
|
+
description = "Rule description"
|
|
34
|
+
author = "Author name"
|
|
35
|
+
|
|
36
|
+
keywords:
|
|
37
|
+
$keyword1 = "exact text"
|
|
38
|
+
$keyword2 = /regex pattern/i
|
|
39
|
+
|
|
40
|
+
semantics:
|
|
41
|
+
$semantic1 = "semantic pattern" (0.6)
|
|
42
|
+
|
|
43
|
+
llm:
|
|
44
|
+
$llm_check = "LLM evaluation prompt" (0.7)
|
|
45
|
+
|
|
46
|
+
condition:
|
|
47
|
+
keywords.$keyword1 or semantics.$semantic1 or llm.$llm_check
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Getting Started
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pip install nova-framework
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
This project is licensed under the [MIT License](LICENSE).
|
|
60
|
+
|
|
61
|
+
## Credits
|
|
62
|
+
|
|
63
|
+
Created and maintained by [fr0gger](https://github.com/fr0gger).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NOVA: The Prompt Pattern Matching
|
|
3
|
+
Author: Thomas Roccia
|
|
4
|
+
twitter: @fr0gger_
|
|
5
|
+
License: MIT License
|
|
6
|
+
Version: 1.0.0
|
|
7
|
+
Description: Main Nova framework package initialization
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__version__ = "1.0.0"
|
|
11
|
+
|
|
12
|
+
from nova.core.rules import (
|
|
13
|
+
KeywordPattern,
|
|
14
|
+
SemanticPattern,
|
|
15
|
+
LLMPattern,
|
|
16
|
+
NovaRule
|
|
17
|
+
)
|
|
18
|
+
from nova.core.matcher import NovaMatcher
|
|
19
|
+
from nova.core.parser import NovaParser
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
'KeywordPattern',
|
|
23
|
+
'SemanticPattern',
|
|
24
|
+
'LLMPattern',
|
|
25
|
+
'NovaRule',
|
|
26
|
+
'NovaMatcher',
|
|
27
|
+
'NovaParser',
|
|
28
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NOVA: The Prompt Pattern Matching
|
|
3
|
+
Author: Thomas Roccia
|
|
4
|
+
twitter: @fr0gger_
|
|
5
|
+
License: MIT License
|
|
6
|
+
Version: 1.0.0
|
|
7
|
+
Description: Core components package initialization
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from nova.core.rules import (
|
|
11
|
+
KeywordPattern,
|
|
12
|
+
SemanticPattern,
|
|
13
|
+
LLMPattern,
|
|
14
|
+
NovaRule
|
|
15
|
+
)
|
|
16
|
+
from nova.core.matcher import NovaMatcher
|
|
17
|
+
from nova.core.parser import NovaParser, NovaRuleFileParser
|
|
18
|
+
from nova.core.scanner import NovaScanner
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
'KeywordPattern',
|
|
22
|
+
'SemanticPattern',
|
|
23
|
+
'LLMPattern',
|
|
24
|
+
'NovaRule',
|
|
25
|
+
'NovaMatcher',
|
|
26
|
+
'NovaParser',
|
|
27
|
+
'NovaRuleFileParser',
|
|
28
|
+
'NovaScanner',
|
|
29
|
+
]
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NOVA: The Prompt Pattern Matching
|
|
3
|
+
Author: Thomas Roccia
|
|
4
|
+
twitter: @fr0gger_
|
|
5
|
+
License: MIT License
|
|
6
|
+
Version: 1.0.0
|
|
7
|
+
Description: Core matcher implementation for Nova rules
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Dict, List, Tuple, Optional, Any
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from nova.core.rules import NovaRule, KeywordPattern, SemanticPattern, LLMPattern
|
|
14
|
+
from nova.evaluators.keywords import DefaultKeywordEvaluator
|
|
15
|
+
from nova.evaluators.semantics import DefaultSemanticEvaluator
|
|
16
|
+
from nova.evaluators.llm import OpenAIEvaluator
|
|
17
|
+
from nova.evaluators.condition import evaluate_condition
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class NovaMatcher:
|
|
21
|
+
"""
|
|
22
|
+
Matcher for Nova rules.
|
|
23
|
+
Evaluates text against rules using different pattern types.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self,
|
|
27
|
+
rule: NovaRule,
|
|
28
|
+
keyword_evaluator: Optional[DefaultKeywordEvaluator] = None,
|
|
29
|
+
semantic_evaluator: Optional[DefaultSemanticEvaluator] = None,
|
|
30
|
+
llm_evaluator: Optional[OpenAIEvaluator] = None):
|
|
31
|
+
"""
|
|
32
|
+
Initialize the matcher with a rule and optional custom evaluators.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
rule: The NovaRule to match against
|
|
36
|
+
keyword_evaluator: Custom keyword evaluator (uses DefaultKeywordEvaluator if None)
|
|
37
|
+
semantic_evaluator: Custom semantic evaluator (uses DefaultSemanticEvaluator if None)
|
|
38
|
+
llm_evaluator: Custom LLM evaluator (uses OpenAIEvaluator if None)
|
|
39
|
+
"""
|
|
40
|
+
self.rule = rule
|
|
41
|
+
|
|
42
|
+
# Initialize evaluators
|
|
43
|
+
self.keyword_evaluator = keyword_evaluator or DefaultKeywordEvaluator()
|
|
44
|
+
self.semantic_evaluator = semantic_evaluator or DefaultSemanticEvaluator()
|
|
45
|
+
self.llm_evaluator = llm_evaluator or OpenAIEvaluator()
|
|
46
|
+
|
|
47
|
+
# Pre-compile keyword patterns for performance
|
|
48
|
+
self._precompile_patterns()
|
|
49
|
+
|
|
50
|
+
def _precompile_patterns(self):
|
|
51
|
+
"""Pre-compile regex patterns for better performance."""
|
|
52
|
+
for key, pattern in self.rule.keywords.items():
|
|
53
|
+
if pattern.is_regex:
|
|
54
|
+
self.keyword_evaluator.compile_pattern(key, pattern)
|
|
55
|
+
|
|
56
|
+
def check_prompt(self, prompt: str) -> Dict[str, Any]:
|
|
57
|
+
"""
|
|
58
|
+
Check if a prompt matches the rule.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
prompt: The prompt text to check
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Dictionary containing match results and details
|
|
65
|
+
"""
|
|
66
|
+
# Get all keyword matches for debugging
|
|
67
|
+
all_keyword_matches = {}
|
|
68
|
+
for key, pattern in self.rule.keywords.items():
|
|
69
|
+
all_keyword_matches[key] = self.keyword_evaluator.evaluate(pattern, prompt, key)
|
|
70
|
+
|
|
71
|
+
# Get all semantic matches for debugging
|
|
72
|
+
all_semantic_matches = {}
|
|
73
|
+
all_semantic_scores = {}
|
|
74
|
+
for key, pattern in self.rule.semantics.items():
|
|
75
|
+
matched, score = self.semantic_evaluator.evaluate(pattern, prompt)
|
|
76
|
+
all_semantic_matches[key] = matched
|
|
77
|
+
all_semantic_scores[key] = score
|
|
78
|
+
|
|
79
|
+
# Get all LLM matches for debugging
|
|
80
|
+
all_llm_matches = {}
|
|
81
|
+
all_llm_scores = {}
|
|
82
|
+
for key, pattern in self.rule.llms.items():
|
|
83
|
+
# Use the pattern's threshold as the temperature parameter
|
|
84
|
+
temperature = pattern.threshold
|
|
85
|
+
matched, confidence, details = self.llm_evaluator.evaluate_prompt(pattern.pattern, prompt, temperature=temperature)
|
|
86
|
+
all_llm_matches[key] = matched # Don't compare confidence to threshold anymore
|
|
87
|
+
all_llm_scores[key] = confidence
|
|
88
|
+
|
|
89
|
+
# For the condition evaluation, only use variables explicitly referenced
|
|
90
|
+
condition = self.rule.condition
|
|
91
|
+
|
|
92
|
+
# Extract variables directly referenced in the condition
|
|
93
|
+
keyword_matches = {}
|
|
94
|
+
semantic_matches = {}
|
|
95
|
+
llm_matches = {}
|
|
96
|
+
|
|
97
|
+
# Check for direct variable references with wildcards (section.$var*)
|
|
98
|
+
for section, var_dict, target_dict in [
|
|
99
|
+
('keywords', all_keyword_matches, keyword_matches),
|
|
100
|
+
('semantics', all_semantic_matches, semantic_matches),
|
|
101
|
+
('llm', all_llm_matches, llm_matches)
|
|
102
|
+
]:
|
|
103
|
+
# Match exact references like "section.$var"
|
|
104
|
+
pattern = rf'{section}\.\$([a-zA-Z0-9_]+)(?!\*)'
|
|
105
|
+
for match in re.finditer(pattern, condition):
|
|
106
|
+
var_name = f"${match.group(1)}"
|
|
107
|
+
if var_name in var_dict:
|
|
108
|
+
target_dict[var_name] = var_dict[var_name]
|
|
109
|
+
|
|
110
|
+
# Match wildcard references like "section.$var*"
|
|
111
|
+
wildcard_pattern = rf'{section}\.\$([a-zA-Z0-9_]+)\*'
|
|
112
|
+
for match in re.finditer(wildcard_pattern, condition):
|
|
113
|
+
prefix = match.group(1)
|
|
114
|
+
# Add all variables matching this prefix
|
|
115
|
+
for var, value in var_dict.items():
|
|
116
|
+
if var[1:].startswith(prefix): # Remove $ from var name
|
|
117
|
+
target_dict[var] = value
|
|
118
|
+
|
|
119
|
+
# Check for standalone variables ($var)
|
|
120
|
+
var_pattern = r'(?<![a-zA-Z0-9_\.])(\$[a-zA-Z0-9_]+)(?!\*)'
|
|
121
|
+
for match in re.finditer(var_pattern, condition):
|
|
122
|
+
var_name = match.group(1)
|
|
123
|
+
|
|
124
|
+
# Skip if it's already handled as a section.$ reference
|
|
125
|
+
if any(var_name in d for d in [keyword_matches, semantic_matches, llm_matches]):
|
|
126
|
+
continue
|
|
127
|
+
|
|
128
|
+
# Try to find where this variable is defined
|
|
129
|
+
if var_name in all_keyword_matches:
|
|
130
|
+
keyword_matches[var_name] = all_keyword_matches[var_name]
|
|
131
|
+
elif var_name in all_semantic_matches:
|
|
132
|
+
semantic_matches[var_name] = all_semantic_matches[var_name]
|
|
133
|
+
elif var_name in all_llm_matches:
|
|
134
|
+
llm_matches[var_name] = all_llm_matches[var_name]
|
|
135
|
+
|
|
136
|
+
# Handle "any of" wildcards if present
|
|
137
|
+
any_of_pattern = r'any\s+of\s+\(\$([a-zA-Z0-9_]+)\*\)'
|
|
138
|
+
for match in re.finditer(any_of_pattern, condition):
|
|
139
|
+
prefix = match.group(1)
|
|
140
|
+
|
|
141
|
+
# Add variables matching this prefix from all sections
|
|
142
|
+
for var, value in all_keyword_matches.items():
|
|
143
|
+
if var[1:].startswith(prefix): # Remove $ from var name
|
|
144
|
+
keyword_matches[var] = value
|
|
145
|
+
|
|
146
|
+
for var, value in all_semantic_matches.items():
|
|
147
|
+
if var[1:].startswith(prefix):
|
|
148
|
+
semantic_matches[var] = value
|
|
149
|
+
|
|
150
|
+
for var, value in all_llm_matches.items():
|
|
151
|
+
if var[1:].startswith(prefix):
|
|
152
|
+
llm_matches[var] = value
|
|
153
|
+
|
|
154
|
+
# Process section wildcards (keywords.*, semantics.*, llm.*)
|
|
155
|
+
if "keywords.*" in condition:
|
|
156
|
+
keyword_matches.update(all_keyword_matches)
|
|
157
|
+
if "semantics.*" in condition:
|
|
158
|
+
semantic_matches.update(all_semantic_matches)
|
|
159
|
+
if "llm.*" in condition:
|
|
160
|
+
llm_matches.update(all_llm_matches)
|
|
161
|
+
|
|
162
|
+
# Evaluate condition if provided
|
|
163
|
+
has_match = False
|
|
164
|
+
condition_result = None
|
|
165
|
+
|
|
166
|
+
if self.rule.condition:
|
|
167
|
+
# Use the condition evaluator with filtered match types
|
|
168
|
+
condition_result = evaluate_condition(
|
|
169
|
+
self.rule.condition,
|
|
170
|
+
keyword_matches,
|
|
171
|
+
semantic_matches,
|
|
172
|
+
llm_matches
|
|
173
|
+
)
|
|
174
|
+
has_match = condition_result
|
|
175
|
+
else:
|
|
176
|
+
# Fall back to original behavior if no condition is specified
|
|
177
|
+
has_match = any(keyword_matches.values()) or any(semantic_matches.values()) or any(llm_matches.values())
|
|
178
|
+
|
|
179
|
+
# Build results with matching variables only
|
|
180
|
+
results = {
|
|
181
|
+
'matched': has_match,
|
|
182
|
+
'rule_name': self.rule.name,
|
|
183
|
+
'meta': self.rule.meta,
|
|
184
|
+
'matching_keywords': {k: v for k, v in keyword_matches.items() if v},
|
|
185
|
+
'matching_semantics': {k: v for k, v in semantic_matches.items() if v},
|
|
186
|
+
'matching_llm': {k: v for k, v in llm_matches.items() if v},
|
|
187
|
+
'semantic_scores': all_semantic_scores,
|
|
188
|
+
'llm_scores': all_llm_scores,
|
|
189
|
+
'debug': {
|
|
190
|
+
'condition': self.rule.condition,
|
|
191
|
+
'condition_result': condition_result,
|
|
192
|
+
'all_keyword_matches': all_keyword_matches,
|
|
193
|
+
'all_semantic_matches': all_semantic_matches,
|
|
194
|
+
'all_llm_matches': all_llm_matches
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return results
|