pyoaev 1.18.20__py3-none-any.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.
- docs/conf.py +65 -0
- pyoaev/__init__.py +26 -0
- pyoaev/_version.py +6 -0
- pyoaev/apis/__init__.py +20 -0
- pyoaev/apis/attack_pattern.py +28 -0
- pyoaev/apis/collector.py +29 -0
- pyoaev/apis/cve.py +18 -0
- pyoaev/apis/document.py +29 -0
- pyoaev/apis/endpoint.py +38 -0
- pyoaev/apis/inject.py +29 -0
- pyoaev/apis/inject_expectation/__init__.py +1 -0
- pyoaev/apis/inject_expectation/inject_expectation.py +118 -0
- pyoaev/apis/inject_expectation/model/__init__.py +7 -0
- pyoaev/apis/inject_expectation/model/expectation.py +173 -0
- pyoaev/apis/inject_expectation_trace.py +36 -0
- pyoaev/apis/injector.py +26 -0
- pyoaev/apis/injector_contract.py +56 -0
- pyoaev/apis/inputs/__init__.py +0 -0
- pyoaev/apis/inputs/search.py +72 -0
- pyoaev/apis/kill_chain_phase.py +22 -0
- pyoaev/apis/me.py +17 -0
- pyoaev/apis/organization.py +11 -0
- pyoaev/apis/payload.py +27 -0
- pyoaev/apis/security_platform.py +33 -0
- pyoaev/apis/tag.py +19 -0
- pyoaev/apis/team.py +25 -0
- pyoaev/apis/user.py +31 -0
- pyoaev/backends/__init__.py +14 -0
- pyoaev/backends/backend.py +136 -0
- pyoaev/backends/protocol.py +32 -0
- pyoaev/base.py +320 -0
- pyoaev/client.py +596 -0
- pyoaev/configuration/__init__.py +3 -0
- pyoaev/configuration/configuration.py +188 -0
- pyoaev/configuration/sources.py +44 -0
- pyoaev/contracts/__init__.py +5 -0
- pyoaev/contracts/contract_builder.py +44 -0
- pyoaev/contracts/contract_config.py +292 -0
- pyoaev/contracts/contract_utils.py +22 -0
- pyoaev/contracts/variable_helper.py +124 -0
- pyoaev/daemons/__init__.py +4 -0
- pyoaev/daemons/base_daemon.py +131 -0
- pyoaev/daemons/collector_daemon.py +91 -0
- pyoaev/exceptions.py +219 -0
- pyoaev/helpers.py +451 -0
- pyoaev/mixins.py +242 -0
- pyoaev/signatures/__init__.py +0 -0
- pyoaev/signatures/signature_match.py +12 -0
- pyoaev/signatures/signature_type.py +51 -0
- pyoaev/signatures/types.py +17 -0
- pyoaev/utils.py +211 -0
- pyoaev-1.18.20.dist-info/METADATA +134 -0
- pyoaev-1.18.20.dist-info/RECORD +72 -0
- pyoaev-1.18.20.dist-info/WHEEL +5 -0
- pyoaev-1.18.20.dist-info/licenses/LICENSE +201 -0
- pyoaev-1.18.20.dist-info/top_level.txt +4 -0
- scripts/release.py +127 -0
- test/__init__.py +0 -0
- test/apis/__init__.py +0 -0
- test/apis/expectation/__init__.py +0 -0
- test/apis/expectation/test_expectation.py +338 -0
- test/apis/injector_contract/__init__.py +0 -0
- test/apis/injector_contract/test_injector_contract.py +58 -0
- test/configuration/__init__.py +0 -0
- test/configuration/test_configuration.py +257 -0
- test/configuration/test_sources.py +69 -0
- test/daemons/__init__.py +0 -0
- test/daemons/test_base_daemon.py +109 -0
- test/daemons/test_collector_daemon.py +39 -0
- test/signatures/__init__.py +0 -0
- test/signatures/test_signature_match.py +25 -0
- test/signatures/test_signature_type.py +57 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from pyoaev.signatures.signature_match import SignatureMatch
|
|
2
|
+
from pyoaev.signatures.types import MatchTypes, SignatureTypes
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SignatureType:
|
|
6
|
+
"""Describes a signature of some time and a matching policy
|
|
7
|
+
|
|
8
|
+
:param label: Type specifier
|
|
9
|
+
:type label: SignatureTypes
|
|
10
|
+
:param match_type: the matching policy to use when trying
|
|
11
|
+
to match this signature type, e.g. fuzzy, simple...
|
|
12
|
+
:type match_type: MatchTypes
|
|
13
|
+
:param match_score: if the matching type is fuzzy, this is
|
|
14
|
+
the score to use as threshold, defaults to None
|
|
15
|
+
:type match_score: int, optional
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
label: SignatureTypes,
|
|
21
|
+
match_type: MatchTypes = MatchTypes.MATCH_TYPE_SIMPLE,
|
|
22
|
+
match_score: int = None,
|
|
23
|
+
):
|
|
24
|
+
self.label = label
|
|
25
|
+
self.match_policy = SignatureMatch(match_type, match_score)
|
|
26
|
+
|
|
27
|
+
def make_struct_for_matching(self, data):
|
|
28
|
+
"""Provided some `data`, formats a dictionary specifying the matching
|
|
29
|
+
policy to use by the helper to match expected signatures (from expectations)
|
|
30
|
+
with actual, alert signatures (from the security software)
|
|
31
|
+
|
|
32
|
+
:param data: arbitrary data, but most often string or a number primitive
|
|
33
|
+
:type: Any
|
|
34
|
+
|
|
35
|
+
:return: dictionary of matching specifiers::
|
|
36
|
+
{
|
|
37
|
+
"type": str,
|
|
38
|
+
"data": any,
|
|
39
|
+
"score": (optional) int
|
|
40
|
+
}
|
|
41
|
+
:rtype: dict
|
|
42
|
+
"""
|
|
43
|
+
struct = {
|
|
44
|
+
"type": self.match_policy.match_type.value,
|
|
45
|
+
"data": data,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if self.match_policy.match_score is not None:
|
|
49
|
+
struct["score"] = self.match_policy.match_score
|
|
50
|
+
|
|
51
|
+
return struct
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class MatchTypes(str, Enum):
|
|
5
|
+
MATCH_TYPE_FUZZY = "fuzzy"
|
|
6
|
+
MATCH_TYPE_SIMPLE = "simple"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class SignatureTypes(str, Enum):
|
|
10
|
+
SIG_TYPE_PARENT_PROCESS_NAME = "parent_process_name"
|
|
11
|
+
SIG_TYPE_SOURCE_IPV4_ADDRESS = "source_ipv4_address"
|
|
12
|
+
SIG_TYPE_SOURCE_IPV6_ADDRESS = "source_ipv6_address"
|
|
13
|
+
SIG_TYPE_TARGET_IPV4_ADDRESS = "target_ipv4_address"
|
|
14
|
+
SIG_TYPE_TARGET_IPV6_ADDRESS = "target_ipv6_address"
|
|
15
|
+
SIG_TYPE_TARGET_HOSTNAME_ADDRESS = "target_hostname_address"
|
|
16
|
+
SIG_TYPE_START_DATE = "start_date"
|
|
17
|
+
SIG_TYPE_END_DATE = "end_date"
|
pyoaev/utils.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import datetime
|
|
3
|
+
import email.message
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import threading
|
|
7
|
+
import urllib.parse
|
|
8
|
+
from typing import Any, Callable, Dict, Iterator, List, Optional, Tuple, Union
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
from pythonjsonlogger import jsonlogger
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _StdoutStream:
|
|
15
|
+
def __call__(self, chunk: Any) -> None:
|
|
16
|
+
print(chunk)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_content_type(content_type: Optional[str]) -> str:
|
|
20
|
+
message = email.message.Message()
|
|
21
|
+
message["content-type"] = content_type
|
|
22
|
+
|
|
23
|
+
return message.get_content_type()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def response_content(
|
|
27
|
+
response: requests.Response,
|
|
28
|
+
streamed: bool,
|
|
29
|
+
action: Optional[Callable[[bytes], None]],
|
|
30
|
+
chunk_size: int,
|
|
31
|
+
*,
|
|
32
|
+
iterator: bool,
|
|
33
|
+
) -> Optional[Union[bytes, Iterator[Any]]]:
|
|
34
|
+
if iterator:
|
|
35
|
+
return response.iter_content(chunk_size=chunk_size)
|
|
36
|
+
|
|
37
|
+
if streamed is False:
|
|
38
|
+
return response.content
|
|
39
|
+
|
|
40
|
+
if action is None:
|
|
41
|
+
action = _StdoutStream()
|
|
42
|
+
|
|
43
|
+
for chunk in response.iter_content(chunk_size=chunk_size):
|
|
44
|
+
if chunk:
|
|
45
|
+
action(chunk)
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def copy_dict(
|
|
50
|
+
*,
|
|
51
|
+
src: Dict[str, Any],
|
|
52
|
+
dest: Dict[str, Any],
|
|
53
|
+
) -> None:
|
|
54
|
+
for k, v in src.items():
|
|
55
|
+
if isinstance(v, dict):
|
|
56
|
+
for dict_k, dict_v in v.items():
|
|
57
|
+
dest[f"{k}[{dict_k}]"] = dict_v
|
|
58
|
+
else:
|
|
59
|
+
dest[k] = v
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class EncodedId(str):
|
|
63
|
+
def __new__(cls, value: Union[str, int, "EncodedId"]) -> "EncodedId":
|
|
64
|
+
if isinstance(value, EncodedId):
|
|
65
|
+
return value
|
|
66
|
+
|
|
67
|
+
if not isinstance(value, (int, str)):
|
|
68
|
+
raise TypeError(f"Unsupported type received: {type(value)}")
|
|
69
|
+
if isinstance(value, str):
|
|
70
|
+
value = urllib.parse.quote(value, safe="")
|
|
71
|
+
return super().__new__(cls, value)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def remove_none_from_dict(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
75
|
+
return {k: v for k, v in data.items() if v is not None}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class EnhancedJSONEncoder(json.JSONEncoder):
|
|
79
|
+
def default(self, o):
|
|
80
|
+
if dataclasses.is_dataclass(o):
|
|
81
|
+
return dataclasses.asdict(o)
|
|
82
|
+
return super().default(o)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclasses.dataclass(frozen=True)
|
|
86
|
+
class RequiredOptional:
|
|
87
|
+
required: Tuple[str, ...] = ()
|
|
88
|
+
optional: Tuple[str, ...] = ()
|
|
89
|
+
exclusive: Tuple[str, ...] = ()
|
|
90
|
+
|
|
91
|
+
def validate_attrs(
|
|
92
|
+
self,
|
|
93
|
+
*,
|
|
94
|
+
data: Dict[str, Any],
|
|
95
|
+
excludes: Optional[List[str]] = None,
|
|
96
|
+
) -> None:
|
|
97
|
+
if excludes is None:
|
|
98
|
+
excludes = []
|
|
99
|
+
|
|
100
|
+
if self.required:
|
|
101
|
+
required = [k for k in self.required if k not in excludes]
|
|
102
|
+
missing = [attr for attr in required if attr not in data]
|
|
103
|
+
if missing:
|
|
104
|
+
raise AttributeError(f"Missing attributes: {', '.join(missing)}")
|
|
105
|
+
|
|
106
|
+
if self.exclusive:
|
|
107
|
+
exclusives = [attr for attr in data if attr in self.exclusive]
|
|
108
|
+
if len(exclusives) > 1:
|
|
109
|
+
raise AttributeError(
|
|
110
|
+
f"Provide only one of these attributes: {', '.join(exclusives)}"
|
|
111
|
+
)
|
|
112
|
+
if not exclusives:
|
|
113
|
+
raise AttributeError(
|
|
114
|
+
f"Must provide one of these attributes: "
|
|
115
|
+
f"{', '.join(self.exclusive)}"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class CustomJsonFormatter(jsonlogger.JsonFormatter):
|
|
120
|
+
def add_fields(self, log_record, record, message_dict):
|
|
121
|
+
super(CustomJsonFormatter, self).add_fields(log_record, record, message_dict)
|
|
122
|
+
if not log_record.get("timestamp"):
|
|
123
|
+
# This doesn't use record.created, so it is slightly off
|
|
124
|
+
now = datetime.datetime.now(datetime.UTC).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
125
|
+
log_record["timestamp"] = now
|
|
126
|
+
if log_record.get("level"):
|
|
127
|
+
log_record["level"] = log_record["level"].upper()
|
|
128
|
+
else:
|
|
129
|
+
log_record["level"] = record.levelname
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def setup_logging_config(level, json_logging=True):
|
|
133
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
|
134
|
+
logging.getLogger("pika").setLevel(logging.ERROR)
|
|
135
|
+
# Exceptions
|
|
136
|
+
if json_logging:
|
|
137
|
+
log_handler = logging.StreamHandler()
|
|
138
|
+
log_handler.setLevel(level)
|
|
139
|
+
formatter = CustomJsonFormatter("%(timestamp)s %(level)s %(name)s %(message)s")
|
|
140
|
+
log_handler.setFormatter(formatter)
|
|
141
|
+
logging.basicConfig(handlers=[log_handler], level=level, force=True)
|
|
142
|
+
else:
|
|
143
|
+
logging.basicConfig(level=level)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class AppLogger:
|
|
147
|
+
def __init__(self, level, json_logging=True, name: str = __name__):
|
|
148
|
+
self.log_level = level
|
|
149
|
+
self.json_logging = json_logging
|
|
150
|
+
setup_logging_config(self.log_level, self.json_logging)
|
|
151
|
+
self.local_logger = logging.getLogger(name)
|
|
152
|
+
|
|
153
|
+
def __call__(self, name):
|
|
154
|
+
self.local_logger = logging.getLogger(name)
|
|
155
|
+
return self
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def prepare_meta(meta=None):
|
|
159
|
+
return None if meta is None else {"attributes": meta}
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def setup_logger_level(lib, log_level):
|
|
163
|
+
logging.getLogger(lib).setLevel(log_level)
|
|
164
|
+
|
|
165
|
+
def debug(self, message, meta=None):
|
|
166
|
+
self.local_logger.debug(message, extra=AppLogger.prepare_meta(meta))
|
|
167
|
+
|
|
168
|
+
def info(self, message, meta=None):
|
|
169
|
+
self.local_logger.info(message, extra=AppLogger.prepare_meta(meta))
|
|
170
|
+
|
|
171
|
+
def warning(self, message, meta=None):
|
|
172
|
+
self.local_logger.warning(message, extra=AppLogger.prepare_meta(meta))
|
|
173
|
+
|
|
174
|
+
def error(self, message, meta=None):
|
|
175
|
+
# noinspection PyTypeChecker
|
|
176
|
+
self.local_logger.error(message, exc_info=1, extra=AppLogger.prepare_meta(meta))
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
# DEPRECATED: compatibility
|
|
180
|
+
def logger(level, json_logging=True):
|
|
181
|
+
return AppLogger(level, json_logging)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class PingAlive(threading.Thread):
|
|
185
|
+
def __init__(self, api, config, logger, ping_type) -> None:
|
|
186
|
+
threading.Thread.__init__(self)
|
|
187
|
+
self.ping_type = ping_type
|
|
188
|
+
self.api = api
|
|
189
|
+
self.config = config
|
|
190
|
+
self.logger = logger
|
|
191
|
+
self.in_error = False
|
|
192
|
+
self.exit_event = threading.Event()
|
|
193
|
+
|
|
194
|
+
def ping(self) -> None:
|
|
195
|
+
while not self.exit_event.is_set():
|
|
196
|
+
try:
|
|
197
|
+
if self.ping_type == "injector":
|
|
198
|
+
self.api.injector.create(self.config, False)
|
|
199
|
+
else:
|
|
200
|
+
self.api.collector.create(self.config, False)
|
|
201
|
+
except Exception as err: # pylint: disable=broad-except
|
|
202
|
+
self.logger.error("Error pinging the API: " + str(err))
|
|
203
|
+
self.exit_event.wait(40)
|
|
204
|
+
|
|
205
|
+
def run(self) -> None:
|
|
206
|
+
self.logger.info("Starting PingAlive thread")
|
|
207
|
+
self.ping()
|
|
208
|
+
|
|
209
|
+
def stop(self) -> None:
|
|
210
|
+
self.logger.info("Preparing PingAlive for clean shutdown")
|
|
211
|
+
self.exit_event.set()
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyoaev
|
|
3
|
+
Version: 1.18.20
|
|
4
|
+
Summary: Python API client for OpenAEV.
|
|
5
|
+
Author-email: Filigran <contact@filigran.io>
|
|
6
|
+
Maintainer-email: Filigran <contact@filigran.io>
|
|
7
|
+
License: Apache-2.0
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Intended Audience :: Information Technology
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: Natural Language :: French
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Security
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: datefinder<0.8,>=0.7.3
|
|
23
|
+
Requires-Dist: pika<1.4.0,>=1.3.0
|
|
24
|
+
Requires-Dist: python-magic<0.5,>=0.4.27; sys_platform == "linux" or sys_platform == "darwin"
|
|
25
|
+
Requires-Dist: python-magic-bin<0.5,>=0.4.14; sys_platform == "win32"
|
|
26
|
+
Requires-Dist: python_json_logger<3.4.0,>=3.3.0
|
|
27
|
+
Requires-Dist: PyYAML<6.1,>=6.0
|
|
28
|
+
Requires-Dist: pydantic<2.12.0,>=2.11.3
|
|
29
|
+
Requires-Dist: requests<2.33.0,>=2.32.3
|
|
30
|
+
Requires-Dist: setuptools<80.10.0,>=80.9.0
|
|
31
|
+
Requires-Dist: cachetools<5.6.0,>=5.5.0
|
|
32
|
+
Requires-Dist: prometheus-client<0.23.0,>=0.22.1
|
|
33
|
+
Requires-Dist: opentelemetry-api<1.36.0,>=1.35.0
|
|
34
|
+
Requires-Dist: opentelemetry-sdk<1.36.0,>=1.35.0
|
|
35
|
+
Requires-Dist: requests-toolbelt<1.1.0,>=1.0.0
|
|
36
|
+
Requires-Dist: dataclasses-json<0.7.0,>=0.6.4
|
|
37
|
+
Requires-Dist: thefuzz<0.23,>=0.22
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: black<25.2.0,>=25.1.0; extra == "dev"
|
|
40
|
+
Requires-Dist: build<1.3.0,>=1.2.1; extra == "dev"
|
|
41
|
+
Requires-Dist: isort<6.1.0,>=6.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: types-pytz<2025.3.0.0,>=2025.2.0.20250326; extra == "dev"
|
|
43
|
+
Requires-Dist: pre-commit<4.3.0,>=4.2.0; extra == "dev"
|
|
44
|
+
Requires-Dist: types-python-dateutil<2.10.0,>=2.9.0; extra == "dev"
|
|
45
|
+
Requires-Dist: wheel<0.46.0,>=0.45.1; extra == "dev"
|
|
46
|
+
Provides-Extra: doc
|
|
47
|
+
Requires-Dist: autoapi<2.1.0,>=2.0.1; extra == "doc"
|
|
48
|
+
Requires-Dist: sphinx-autodoc-typehints<3.3.0,>=3.2.0; extra == "doc"
|
|
49
|
+
Requires-Dist: sphinx-rtd-theme<3.1.0,>=3.0.2; extra == "doc"
|
|
50
|
+
Dynamic: license-file
|
|
51
|
+
|
|
52
|
+
# OpenAEV client for Python
|
|
53
|
+
|
|
54
|
+
[](https://openaev.io)
|
|
55
|
+
[](https://circleci.com/gh/OpenAEV-Platform/client-python/tree/main)
|
|
56
|
+
[](https://openaev-client-for-python.readthedocs.io/en/latest/)
|
|
57
|
+
[](https://github.com/OpenAEV-Platform/client-python/releases/latest)
|
|
58
|
+
[](https://pypi.python.org/pypi/pyoaev/)
|
|
59
|
+
[](https://community.filigran.io)
|
|
60
|
+
|
|
61
|
+
The official OpenAEV Python client helps developers to use the OpenAEV API by providing easy to use methods and utils.
|
|
62
|
+
This client is also used by some OpenAEV components.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
To install the latest Python client library, please use `pip`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
$ pip3 install pyoaev
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Local development
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Fork the current repository, then clone your fork
|
|
76
|
+
$ git clone https://github.com/YOUR-USERNAME/client-python
|
|
77
|
+
$ cd client-python
|
|
78
|
+
$ git remote add upstream https://github.com/OpenAEV-Platform/client-python.git
|
|
79
|
+
# Create a branch for your feature/fix
|
|
80
|
+
$ git checkout -b [branch-name]
|
|
81
|
+
# Create a virtualenv
|
|
82
|
+
$ python3 -m venv .venv
|
|
83
|
+
$ source .venv/bin/activate
|
|
84
|
+
# Install the client-python and dependencies for the development and the documentation
|
|
85
|
+
$ python3 -m pip install -e .[dev,doc]
|
|
86
|
+
# Set up the git hook scripts
|
|
87
|
+
$ pre-commit install
|
|
88
|
+
# Create your feature/fix
|
|
89
|
+
# Create tests for your changes
|
|
90
|
+
$ python -m unittest
|
|
91
|
+
# Push you feature/fix on Github
|
|
92
|
+
$ git add [file(s)]
|
|
93
|
+
$ git commit -m "[descriptive message]"
|
|
94
|
+
$ git push origin [branch-name]
|
|
95
|
+
# Open a pull request
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Install the package locally
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
$ pip install -e .
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Documentation
|
|
105
|
+
|
|
106
|
+
### Client usage
|
|
107
|
+
|
|
108
|
+
To learn about how to use the OpenAEV Python client and read some examples and cases, refer to [the client documentation](https://openaev-client-for-python.readthedocs.io/en/latest/client_usage/getting_started.html).
|
|
109
|
+
|
|
110
|
+
### API reference
|
|
111
|
+
|
|
112
|
+
To learn about the methods available for executing queries and retrieving their answers, refer to [the client API Reference](https://openaev-client-for-python.readthedocs.io/en/latest/pyoaev/pyoaev.html).
|
|
113
|
+
|
|
114
|
+
## Tests
|
|
115
|
+
|
|
116
|
+
The standard `unittest` library is used for running the tests.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
$ python -m unittest
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Code Coverage
|
|
123
|
+
|
|
124
|
+
To run the tests and generate a code coverage report:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pytest --cov=. tests/
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## About
|
|
131
|
+
|
|
132
|
+
OpenAEV is a product designed and developed by the company [Filigran](https://filigran.io).
|
|
133
|
+
|
|
134
|
+
<a href="https://filigran.io" alt="Filigran"><img src="https://github.com/OpenAEV-Platform/openaev/raw/master/.github/img/logo_filigran.png" width="300" /></a>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
docs/conf.py,sha256=_030QD2TV3WgBr8WUpx8HMSSj7NMJMsRFWN7MZ06zI0,2159
|
|
2
|
+
pyoaev/__init__.py,sha256=0fl-Jgp4pcrojfk_FCZ-BIn0KFdGvin9pg3nQ14_uZg,641
|
|
3
|
+
pyoaev/_version.py,sha256=8iMS-Y9389dC7HtReHJGNnoDgyfNbA3m-V32pCvIaz8,185
|
|
4
|
+
pyoaev/base.py,sha256=nJh2Hg9Aw4UcjMrp0nklG5cqHHOKmMPJBRwV0UUNZO0,10756
|
|
5
|
+
pyoaev/client.py,sha256=IEno-WRNQcqLyA_ex7SbnniB8OMuWOIsNZ1gJ7fb63M,22342
|
|
6
|
+
pyoaev/exceptions.py,sha256=3LIn8wmgPlBuxqO-naOPaEi4-bs05IjFILW-J4n594M,7916
|
|
7
|
+
pyoaev/helpers.py,sha256=n4Z5Eq5kiZg0nA-K8cu35j_0vWRHWXBmUCrX5YCx2Aw,17185
|
|
8
|
+
pyoaev/mixins.py,sha256=inqfNP5l4vmc_npA3w2j3DCUI5iJBvqZdnPeXEnLBUo,7506
|
|
9
|
+
pyoaev/utils.py,sha256=Hph7ly0PVyEadkRJI_bRbXF-iwkb_XjFeglAei-myDA,6617
|
|
10
|
+
pyoaev/apis/__init__.py,sha256=9U8sfFKoxncUtkNpz7qHrq6u-kKynIiybQR2MPptQHc,874
|
|
11
|
+
pyoaev/apis/attack_pattern.py,sha256=Qq0rz9C8xPZEde4B73O8tXwDNuymLv3pVPMVaHZC3Kc,772
|
|
12
|
+
pyoaev/apis/collector.py,sha256=BW5gtwPuqFbmQ5sxFa2xJ50Rtab9IN6N8Z4SmaOsBLo,841
|
|
13
|
+
pyoaev/apis/cve.py,sha256=aFubPmfY0MG__I-sZhM2WqOqL6BqRH0ZgkToKlUgjEw,471
|
|
14
|
+
pyoaev/apis/document.py,sha256=KTwV7oMMsIy-MhY05GTORlRlBlOxxW4wEdMwPChwYtQ,851
|
|
15
|
+
pyoaev/apis/endpoint.py,sha256=2gCCjDE9Hd2bDjl_1__bUCn3WSOm8yMGVLZMlm29eTE,1113
|
|
16
|
+
pyoaev/apis/inject.py,sha256=cdSFXe-Gd6RZfTYCJ4ZnH0MDk5YfesMJjZIH9LQ98Ag,890
|
|
17
|
+
pyoaev/apis/inject_expectation_trace.py,sha256=QeYbleKOky7Cvt-oIN9vMCgOYM-LwlmzgoTVa3X64Ms,1101
|
|
18
|
+
pyoaev/apis/injector.py,sha256=tFcu2kcmlLpQUyG5CeNE3m5j7PLbao0paiY-MDFyuNo,722
|
|
19
|
+
pyoaev/apis/injector_contract.py,sha256=Jc-sbuo_P9oVIUDNeoLkoa7Eehd3NzEqDxc7J16LH-g,1793
|
|
20
|
+
pyoaev/apis/kill_chain_phase.py,sha256=vlv4u_6UnCH4eojHyWAG65xlgqka11Y8dPVNieLFpuY,635
|
|
21
|
+
pyoaev/apis/me.py,sha256=Fxlsnn8MuQva2910XhipX95V3GjiIQObZgj0DA5h9dA,397
|
|
22
|
+
pyoaev/apis/organization.py,sha256=TCR1iiGnDVCsGm0zbVRcC3ARv7qzTukkLP1157CmOfI,263
|
|
23
|
+
pyoaev/apis/payload.py,sha256=8XiZpX87VlcC2paM_XfhbIcJfDJdTcbDISw2Jkt5LFs,822
|
|
24
|
+
pyoaev/apis/security_platform.py,sha256=u2UN5yKHiHYNHGKwbai4S9WYDfASqZ0BNkqI2VBx2K8,990
|
|
25
|
+
pyoaev/apis/tag.py,sha256=r_UpNsBlVtzMqbfLxFtU5RDihlQVHo2qz8X5B_uhGhg,492
|
|
26
|
+
pyoaev/apis/team.py,sha256=PZ1D4WHR6MLhAzClrFOaRuzpRq5IZNx3yCHipM-A6f4,788
|
|
27
|
+
pyoaev/apis/user.py,sha256=Fr821Cu3V9B1U9p4oNKZ5u2feHeYe5669Oc55E3G_Og,893
|
|
28
|
+
pyoaev/apis/inject_expectation/__init__.py,sha256=jPlnzxfT31MhmUgEvR4pBAuyG_ciX23-wMKYT5qX5Dc,53
|
|
29
|
+
pyoaev/apis/inject_expectation/inject_expectation.py,sha256=if-OD0uVERQKPExi9mN2CBjlbeqh6T3tP49A4-9nqJU,4217
|
|
30
|
+
pyoaev/apis/inject_expectation/model/__init__.py,sha256=CloFGul64uYDxEdKJr6eZzlFr1eVPb95cqTnRPr0AEU,191
|
|
31
|
+
pyoaev/apis/inject_expectation/model/expectation.py,sha256=Uq0pIdLpXJABX2WNwi3Vj9b4Ygs0D30Xrr2SR0vzdpA,6323
|
|
32
|
+
pyoaev/apis/inputs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
+
pyoaev/apis/inputs/search.py,sha256=DqB7HEtqZg1aS1PwuaC-MatU-vcH0hanNtt6eZMXjrI,2204
|
|
34
|
+
pyoaev/backends/__init__.py,sha256=Y3tsbazJZB7Fme52fCGGprn3-UdQQjalIJ1hansjvzw,272
|
|
35
|
+
pyoaev/backends/backend.py,sha256=4U2Ver8rQd49VqRjb-94fmUJxOeqrBblwpFeHXUCFeI,4328
|
|
36
|
+
pyoaev/backends/protocol.py,sha256=m5qSz1o3i0H4XJCWnqx0wIFilOIU9cKxzFsYxLL6Big,842
|
|
37
|
+
pyoaev/configuration/__init__.py,sha256=cifJWZdzsvv6n9AjGGWTiOOQf_-FAak0QIzSb2L7d78,70
|
|
38
|
+
pyoaev/configuration/configuration.py,sha256=i4DaCfCgKSz5DiQPCUIcw6N-BO8rYoKbWZcZhcbDY0Q,6905
|
|
39
|
+
pyoaev/configuration/sources.py,sha256=09B4X3nUvlRIv1fcAZGZBP-ScV-AOyVrYuLGzMT1zWg,1459
|
|
40
|
+
pyoaev/contracts/__init__.py,sha256=vt5JcjJquIEBt1Bwr1FfvTL9gJQHAO46eYPRG65ehzk,103
|
|
41
|
+
pyoaev/contracts/contract_builder.py,sha256=nZ8E2TcvYq6f_diS4pjshdnldZNmljETnAYAS5G-oW8,1305
|
|
42
|
+
pyoaev/contracts/contract_config.py,sha256=uthRpbvrNYJHVGj_ChSOm8C-aSKtn34gyrCeY_aILjA,7824
|
|
43
|
+
pyoaev/contracts/contract_utils.py,sha256=htaXoG9ndBOYhFsoBXrGludPU79d4aUOejc-I0SSiPU,451
|
|
44
|
+
pyoaev/contracts/variable_helper.py,sha256=2YKCzs_qco0JBRhGYuiMh_aIB6sFJIIzj_CRkx-QLLo,4208
|
|
45
|
+
pyoaev/daemons/__init__.py,sha256=IdR-AYxEjpMIst0Y1oqsmKw4-mc5RSOmGtpxVM4cLHY,127
|
|
46
|
+
pyoaev/daemons/base_daemon.py,sha256=1EssjncVGO57TridSHZ4OBClgi6pYwjIAYSlDrrjxQg,5034
|
|
47
|
+
pyoaev/daemons/collector_daemon.py,sha256=DX9rhHYIEcJq_8vLSWzaWyCgOofWpZ0-YNQ3FviIue8,3783
|
|
48
|
+
pyoaev/signatures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
+
pyoaev/signatures/signature_match.py,sha256=EclhdT2QSf19rr76IxhcISgqXCSU9bvJHQ2Bt7Z-Uek,485
|
|
50
|
+
pyoaev/signatures/signature_type.py,sha256=E-tn_Fhftssf7v-rQy0EbPvsUAvCTTIrlG7LbOsouDo,1684
|
|
51
|
+
pyoaev/signatures/types.py,sha256=uDA_8u2t1qdHQdue8hiw2sO59yahbfL2uDKUoQhktx0,576
|
|
52
|
+
pyoaev-1.18.20.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
53
|
+
scripts/release.py,sha256=2ZBVqpGqajYNLkDOiVAtRf3DvDe97umErVAgEav2EP4,4387
|
|
54
|
+
test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
+
test/apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
+
test/apis/expectation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
57
|
+
test/apis/expectation/test_expectation.py,sha256=BNdZpIjwsiuOCHsdCf0cGcf7Ck-tQ49yxvTXbS6n89w,11500
|
|
58
|
+
test/apis/injector_contract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
59
|
+
test/apis/injector_contract/test_injector_contract.py,sha256=d_EPy8bMVzNGoEL2xlsiZE_6Kz0lEMfVJp9t8Ci9BrY,1555
|
|
60
|
+
test/configuration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
+
test/configuration/test_configuration.py,sha256=soJblSpiZ5HenfQchVy8k93i8t3LUDjd2yfl-YFacow,8132
|
|
62
|
+
test/configuration/test_sources.py,sha256=PbViObmq4I1v0GDyKu8cv-q5UouMj-CErSugii85a70,2261
|
|
63
|
+
test/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
+
test/daemons/test_base_daemon.py,sha256=DWOHIBMSFEz02Jxz5VCtNgS8OAPlJK0EmQT-ucmWUn8,3225
|
|
65
|
+
test/daemons/test_collector_daemon.py,sha256=Z2zzpzj1TVQuLXJY1cKptPihv0AGumraI4bPtwXnaTM,1288
|
|
66
|
+
test/signatures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
|
+
test/signatures/test_signature_match.py,sha256=2iT5M_EnUE2OKE8xe_Jl-Le6gsuQlW3VMAd1iaFGt04,793
|
|
68
|
+
test/signatures/test_signature_type.py,sha256=EznFiGxzKyIiydpB9M6Ytj5P_5emgK3AMQ6lIbvxmZA,2413
|
|
69
|
+
pyoaev-1.18.20.dist-info/METADATA,sha256=1I6gxH-_tZsOqiCm2kUwzvITJG013eSBa869_QU-DnE,5155
|
|
70
|
+
pyoaev-1.18.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
71
|
+
pyoaev-1.18.20.dist-info/top_level.txt,sha256=je2hy1nZbVjTQzH55BQC6Lmduwyu9FOowXzx7NH7Ceg,25
|
|
72
|
+
pyoaev-1.18.20.dist-info/RECORD,,
|