boreal-python 0.1.0__cp311-abi3-win32.whl
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.
- boreal/__init__.py +5 -0
- boreal/__init__.pyi +291 -0
- boreal/boreal.pyd +0 -0
- boreal/py.typed +0 -0
- boreal_python-0.1.0.dist-info/METADATA +84 -0
- boreal_python-0.1.0.dist-info/RECORD +7 -0
- boreal_python-0.1.0.dist-info/WHEEL +4 -0
boreal/__init__.py
ADDED
boreal/__init__.pyi
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
from typing import Any, Callable, Protocol, TypeAlias, TypedDict, final
|
|
2
|
+
from collections import abc
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def compile(
|
|
6
|
+
filepath: str | None = None,
|
|
7
|
+
filepaths: dict[str, str] | None = None,
|
|
8
|
+
source: str | None = None,
|
|
9
|
+
sources: dict[str, str] | None = None,
|
|
10
|
+
file: Readable | None = None,
|
|
11
|
+
|
|
12
|
+
externals: dict[str, ExternalValue] | None = None,
|
|
13
|
+
includes: bool = True,
|
|
14
|
+
error_on_warning: bool = False,
|
|
15
|
+
include_callback: IncludeCallback | None = None,
|
|
16
|
+
strict_escape: bool | None = None,
|
|
17
|
+
profile: CompilerProfile | None = None,
|
|
18
|
+
) -> Scanner: ...
|
|
19
|
+
|
|
20
|
+
def load(
|
|
21
|
+
filepath: str | None = None,
|
|
22
|
+
file: Readable | None = None,
|
|
23
|
+
data: bytes | None = None,
|
|
24
|
+
) -> Scanner: ...
|
|
25
|
+
|
|
26
|
+
def set_config(
|
|
27
|
+
max_strings_per_rule: int | None = None,
|
|
28
|
+
max_match_data: int | None = None,
|
|
29
|
+
stack_size: int | None = None,
|
|
30
|
+
yara_compatibility: bool | None = None,
|
|
31
|
+
) -> None: ...
|
|
32
|
+
|
|
33
|
+
modules: list[str]
|
|
34
|
+
"""List of availables modules"""
|
|
35
|
+
|
|
36
|
+
class Error(Exception): ...
|
|
37
|
+
class AddRuleError(Error): ...
|
|
38
|
+
class SyntaxError(Error): ...
|
|
39
|
+
class ScanError(Error): ...
|
|
40
|
+
class TimeoutError(Error): ...
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@final
|
|
44
|
+
class Rule:
|
|
45
|
+
identifier: str
|
|
46
|
+
namespace: str
|
|
47
|
+
tags: list[str]
|
|
48
|
+
meta: dict[str, MetadataValue]
|
|
49
|
+
is_global: bool
|
|
50
|
+
is_private: bool
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@final
|
|
54
|
+
class Match:
|
|
55
|
+
rule: str
|
|
56
|
+
namespace: str
|
|
57
|
+
meta: dict[str, MetadataValue]
|
|
58
|
+
tags: list[str]
|
|
59
|
+
strings: list[StringMatches]
|
|
60
|
+
|
|
61
|
+
def __le__(self, other: object) -> bool: ...
|
|
62
|
+
def __eq__(self, other: object) -> bool: ...
|
|
63
|
+
def __ne__(self, other: object) -> bool: ...
|
|
64
|
+
def __gt__(self, other: object) -> bool: ...
|
|
65
|
+
def __ge__(self, other: object) -> bool: ...
|
|
66
|
+
|
|
67
|
+
def __hash__(self) -> int: ...
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@final
|
|
71
|
+
class Scanner(abc.Iterable[Rule]):
|
|
72
|
+
warnings: list[str]
|
|
73
|
+
|
|
74
|
+
def match(
|
|
75
|
+
self,
|
|
76
|
+
filepath: str | None = None,
|
|
77
|
+
data: str | bytes | None = None,
|
|
78
|
+
pid: int | None = None,
|
|
79
|
+
externals: dict[str, ExternalValue] | None = None,
|
|
80
|
+
callback: MatchCallback | None = None,
|
|
81
|
+
# TODO: improve type
|
|
82
|
+
which_callbacks: int | None = None,
|
|
83
|
+
fast: bool | None = None,
|
|
84
|
+
timeout: int | None = None,
|
|
85
|
+
modules_data: dict[str, Any] | None = None,
|
|
86
|
+
modules_callback: ModulesCallback | None = None,
|
|
87
|
+
warnings_callback: WarningCallback | None = None,
|
|
88
|
+
console_callback: ConsoleCallback | None = None,
|
|
89
|
+
allow_duplicate_metadata: bool | None = False,
|
|
90
|
+
) -> list[Match]: ...
|
|
91
|
+
|
|
92
|
+
def save(
|
|
93
|
+
self,
|
|
94
|
+
filepath: str | None = None,
|
|
95
|
+
file: Writable | None = None,
|
|
96
|
+
to_bytes: bool = False
|
|
97
|
+
) -> bytes | None: ...
|
|
98
|
+
|
|
99
|
+
def set_params(
|
|
100
|
+
self,
|
|
101
|
+
use_mmap: bool | None = None,
|
|
102
|
+
string_max_nb_matches: int | None = None,
|
|
103
|
+
fragmented_scan_mode: str | None = None,
|
|
104
|
+
process_memory: bool | None = None,
|
|
105
|
+
max_fetched_region_size: int | None = None,
|
|
106
|
+
memory_chunk_size: int | None = None,
|
|
107
|
+
) -> None: ...
|
|
108
|
+
|
|
109
|
+
def __iter__(self) -> RulesIter: ...
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@final
|
|
113
|
+
class RulesIter(abc.Iterator[Rule]):
|
|
114
|
+
def __iter__(self) -> RulesIter: ...
|
|
115
|
+
def __next__(self) -> Rule: ...
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@final
|
|
119
|
+
class StringMatchInstance:
|
|
120
|
+
offset: int
|
|
121
|
+
matched_data: bytes
|
|
122
|
+
matched_length: int
|
|
123
|
+
xor_key: int
|
|
124
|
+
|
|
125
|
+
def plaintext(self) -> bytes: ...
|
|
126
|
+
|
|
127
|
+
def __hash__(self) -> int: ...
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@final
|
|
131
|
+
class StringMatches:
|
|
132
|
+
identifier: str
|
|
133
|
+
instances: list[StringMatchInstance]
|
|
134
|
+
|
|
135
|
+
def is_xor(self) -> bool: ...
|
|
136
|
+
|
|
137
|
+
def __hash__(self) -> int: ...
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@final
|
|
141
|
+
class RuleString:
|
|
142
|
+
namespace: str
|
|
143
|
+
rule: str
|
|
144
|
+
string: str
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@final
|
|
148
|
+
class CompilerProfile:
|
|
149
|
+
Speed: 'CompilerProfile'
|
|
150
|
+
Memory: 'CompilerProfile'
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class RuleDetails(TypedDict):
|
|
154
|
+
"""Details about a rule passed to the match callback."""
|
|
155
|
+
|
|
156
|
+
rule: str
|
|
157
|
+
"""Name of the matching rule"""
|
|
158
|
+
namespace: str
|
|
159
|
+
"""Namespace of the matching rule"""
|
|
160
|
+
meta: dict[str, MetadataValue]
|
|
161
|
+
"""List of tags associated to the rule"""
|
|
162
|
+
tags: list[str]
|
|
163
|
+
"""Dictionary with metadata associated to the rule"""
|
|
164
|
+
strings: list[StringMatches]
|
|
165
|
+
"""Details about the string matches of the rule"""
|
|
166
|
+
matches: bool
|
|
167
|
+
"""Did the rule match"""
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
__version__: str
|
|
171
|
+
"""Version of the boreal-py library"""
|
|
172
|
+
|
|
173
|
+
CALLBACK_CONTINUE: int
|
|
174
|
+
"""Return value used in callbacks to signal the scan must continue.
|
|
175
|
+
|
|
176
|
+
Callbacks used in the [`match`](api.md#boreal.Scanner.match) method should return
|
|
177
|
+
this value to keep the scan going.
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
CALLBACK_ABORT: int
|
|
181
|
+
"""Return value used in callbacks to abort the scan.
|
|
182
|
+
|
|
183
|
+
Callbacks used in the [`match`](api.md#boreal.Scanner.match) method should return
|
|
184
|
+
this value to abort the scan. If the scan is aborted, the match method will
|
|
185
|
+
not raise any exception but will end immediately, returning the results it
|
|
186
|
+
has computed so far.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
CALLBACK_MATCHES: int
|
|
190
|
+
"""Call the match callback when a rule matches.
|
|
191
|
+
|
|
192
|
+
If specified in the `which_callbacks` parameter of the
|
|
193
|
+
[`match`](api.md#boreal.Scanner.match) method, the callback will be
|
|
194
|
+
called when a rule matches.
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
CALLBACK_NON_MATCHES: int
|
|
198
|
+
"""Call the match callback when a rule does not match.
|
|
199
|
+
|
|
200
|
+
If specified in the `which_callbacks` parameter of the
|
|
201
|
+
[`match`](api.md#boreal.Scanner.match) method, the callback will be
|
|
202
|
+
called when a rule does not match.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
CALLBACK_ALL: int
|
|
206
|
+
"""Call the match callback after a rule is evaluated.
|
|
207
|
+
|
|
208
|
+
If specified in the `which_callbacks` parameter of the
|
|
209
|
+
[`match`](api.md#boreal.Scanner.match) method the callback will be called
|
|
210
|
+
after a is evaluated, regardless of whether it has matched or not. the
|
|
211
|
+
[`matches`](api.md#boreal.RuleDetails.matches) attribute of the passed rule
|
|
212
|
+
can be used to know if the rule has matched or not.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
CALLBACK_TOO_MANY_MATCHES: int
|
|
216
|
+
"""A string has had too many matches.
|
|
217
|
+
|
|
218
|
+
This is used in the `warnings_callback` of the [`match`](#boreal.Scanner.match)
|
|
219
|
+
method to indicate the warning kind.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
CallbackResult: TypeAlias = int
|
|
223
|
+
"""Return status that can be returned by a callback.
|
|
224
|
+
|
|
225
|
+
This must be one of:
|
|
226
|
+
|
|
227
|
+
- [`CALLBACK_CONTINUE`](api.md#CALLBACK_CONTINUE)
|
|
228
|
+
|
|
229
|
+
- [`CALLBACK_ABORT`](api.md#boreal.CALLBACK_ABORT)
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
WarningType: TypeAlias = int
|
|
233
|
+
"""Type of warning passed to the warning callback.
|
|
234
|
+
|
|
235
|
+
This can be one of:
|
|
236
|
+
|
|
237
|
+
- [`CALLBACK_TOO_MANY_MATCHES`](api.md#boreal.CALLBACK_TOO_MANY_MATCHES):
|
|
238
|
+
the associated data is a [`RuleString`](api.md#boreal.RuleString).
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
ConsoleCallback: TypeAlias = Callable[[str], None]
|
|
242
|
+
"""Callback handling uses of the `console` module in rules.
|
|
243
|
+
|
|
244
|
+
It receives the log as the lone argument.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
MatchCallback: TypeAlias = Callable[[RuleDetails], CallbackResult]
|
|
248
|
+
"""Callback called when rules are evaluated."""
|
|
249
|
+
|
|
250
|
+
ModulesCallback: TypeAlias = Callable[[dict[str, Any]], CallbackResult]
|
|
251
|
+
"""Callback called when a module is evaluated.
|
|
252
|
+
|
|
253
|
+
The callback receives the dynamic values of the module as the first
|
|
254
|
+
argument. The name of the module is accessible with the `"module"` key.
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
WarningCallback: TypeAlias = Callable[[WarningType, RuleString], CallbackResult]
|
|
258
|
+
"""Callback called when a warning is emitted during a scan."""
|
|
259
|
+
|
|
260
|
+
MetadataValue: TypeAlias = bytes | int | bool
|
|
261
|
+
"""The value of a metadata key declared in a rule."""
|
|
262
|
+
|
|
263
|
+
ExternalValue: TypeAlias = str | bytes | int | float | bool
|
|
264
|
+
"""The value of an external symbol usable in a rule condition."""
|
|
265
|
+
|
|
266
|
+
IncludeCallback: TypeAlias = Callable[[str, str | None, str], str]
|
|
267
|
+
"""Callback used to resolve include directives.
|
|
268
|
+
|
|
269
|
+
Receive three arguments:
|
|
270
|
+
|
|
271
|
+
- The path being included.
|
|
272
|
+
|
|
273
|
+
- The path of the current document. Can be None if the current
|
|
274
|
+
document was specified as a string, such as when using the
|
|
275
|
+
`source` or `sources` parameter.
|
|
276
|
+
|
|
277
|
+
- The current namespace.
|
|
278
|
+
|
|
279
|
+
Must return a string which is the included document.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
class Readable(Protocol):
|
|
283
|
+
"""A readable object"""
|
|
284
|
+
|
|
285
|
+
def read(self) -> str: ...
|
|
286
|
+
|
|
287
|
+
class Writable(Protocol):
|
|
288
|
+
"""A writable object"""
|
|
289
|
+
|
|
290
|
+
def write(self, data: bytes) -> int: ...
|
|
291
|
+
def flush(self) -> None: ...
|
boreal/boreal.pyd
ADDED
|
Binary file
|
boreal/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: boreal-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bindings to the boreal YARA scanner
|
|
5
|
+
Keywords: boreal,yara,string-matching,scan,python
|
|
6
|
+
License: MIT OR Apache-2.0
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
9
|
+
Project-URL: Source Code, https://github.com/vthib/boreal
|
|
10
|
+
|
|
11
|
+
# Python bindings for the boreal YARA scanner
|
|
12
|
+
|
|
13
|
+
The library allows using the [boreal](https://github.com/vthib/boreal) library
|
|
14
|
+
to scan files and processes using YARA rules.
|
|
15
|
+
|
|
16
|
+
```py
|
|
17
|
+
import boreal
|
|
18
|
+
|
|
19
|
+
scanner = boreal.compile(source="""
|
|
20
|
+
rule example {
|
|
21
|
+
meta:
|
|
22
|
+
description = "This is an YARA rule example"
|
|
23
|
+
date = "2022-11-11"
|
|
24
|
+
strings:
|
|
25
|
+
$s1 = { 78 6d 6c 68 74 74 70 2e 73 65 6e 64 28 29 }
|
|
26
|
+
$s2 = "tmp.dat" fullword wide
|
|
27
|
+
condition:
|
|
28
|
+
any of them
|
|
29
|
+
}
|
|
30
|
+
""");
|
|
31
|
+
|
|
32
|
+
results = scanner.match(data=b"<\0t\0m\0p\0.\0d\0a\0t\0>\0")
|
|
33
|
+
assert [rule.name for rule in results] == ["example"]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Description
|
|
37
|
+
|
|
38
|
+
This library can serve as a drop-in replacement of the YARA python library,
|
|
39
|
+
while also providing improvements and saner default behavior.
|
|
40
|
+
|
|
41
|
+
- Literal replacement to the yara library:
|
|
42
|
+
replace `import yara` with `import boreal` and everything will work.
|
|
43
|
+
|
|
44
|
+
- Saner default behavior compared to the yara library: fast scanning enabled
|
|
45
|
+
by default, proper hash implementations of python objects, use of the bytes
|
|
46
|
+
type in some places to avoid losing information, etc.
|
|
47
|
+
|
|
48
|
+
- 100% compatibility with the yara library guaranteed if needed through
|
|
49
|
+
a yara compatibility mode.
|
|
50
|
+
|
|
51
|
+
## Yara compatibility
|
|
52
|
+
|
|
53
|
+
This library guarantees 100% compatibility with the YARA library: the whole API
|
|
54
|
+
is entirely tested against both libraries to guarantee perfect compatibility.
|
|
55
|
+
|
|
56
|
+
However, a few differences are introduced in the default behavior of this library
|
|
57
|
+
to ensure that this default behavior fixes some issues in the behavior of the yara
|
|
58
|
+
library. Those changes are minimal, but can introduce breakage when replacing
|
|
59
|
+
the yara library.
|
|
60
|
+
|
|
61
|
+
Therefore, you can either:
|
|
62
|
+
|
|
63
|
+
- Use the compatibility mode to ensure 100% compatibility with the yara library:
|
|
64
|
+
|
|
65
|
+
```py
|
|
66
|
+
import boreal
|
|
67
|
+
|
|
68
|
+
boreal.set_config(yara_compatibility=True)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This guarantees that the yara library can be replaced and nothing will break.
|
|
72
|
+
However, it also keeps alive a few issues in this library. It is therefore
|
|
73
|
+
only recommended to enable this mode when replacing the yara library and wanting
|
|
74
|
+
to ensure that nothing can break.
|
|
75
|
+
|
|
76
|
+
- Use boreal as is. This fixes a few issues while still providing almost
|
|
77
|
+
entirely the same API.
|
|
78
|
+
|
|
79
|
+
This is recommended if using this library from scratch, or when all the uses
|
|
80
|
+
of the yara library can be easily checked to ensure nothing will break.
|
|
81
|
+
|
|
82
|
+
For a description of all the differences that exists when the compatibility mode
|
|
83
|
+
is not enabled, you can consult [this documentation](https://vthib.github.io/boreal/boreal-py/dev/yara_compatibility_mode/).
|
|
84
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
boreal/__init__.py,sha256=7pK5mFoG5aGo1_qWJrjD6PJK2WQeo8-Epqelbf2G6Z4,107
|
|
2
|
+
boreal/__init__.pyi,sha256=_c9UZrqKNp-Cv8VRdfZPseSw37vyR6VyJQunRYD2Hq0,8117
|
|
3
|
+
boreal/boreal.pyd,sha256=GGaQOuF3iy4wlXI2qDtR8gBSQUxcRSxnTXLIaeRt_6w,4165632
|
|
4
|
+
boreal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
boreal_python-0.1.0.dist-info/METADATA,sha256=OYz1ija9JWpaEPXzcCPuvhU1ck2tWrhBVRzJOPCA65M,2953
|
|
6
|
+
boreal_python-0.1.0.dist-info/WHEEL,sha256=LY5smkqoIcIuBjTsWXyz8S4vZc5pMCWhbi_9Y5gZIWA,91
|
|
7
|
+
boreal_python-0.1.0.dist-info/RECORD,,
|