crowdsec-tracker-api 1.92.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.
- crowdsec_tracker_api-1.92.0/LICENSE +21 -0
- crowdsec_tracker_api-1.92.0/PKG-INFO +35 -0
- crowdsec_tracker_api-1.92.0/README.md +21 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/__init__.py +59 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/base_model.py +58 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/http_client.py +153 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/models.py +945 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/services/__init__.py +0 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/services/cves.py +179 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api/services/integrations.py +172 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api.egg-info/PKG-INFO +35 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api.egg-info/SOURCES.txt +15 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api.egg-info/dependency_links.txt +1 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api.egg-info/requires.txt +3 -0
- crowdsec_tracker_api-1.92.0/crowdsec_tracker_api.egg-info/top_level.txt +1 -0
- crowdsec_tracker_api-1.92.0/pyproject.toml +24 -0
- crowdsec_tracker_api-1.92.0/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Crowdsec
|
|
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,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: crowdsec_tracker_api
|
|
3
|
+
Version: 1.92.0
|
|
4
|
+
Summary: This is the API to manage Crowdsec Live Exploit Tracker service
|
|
5
|
+
Author-email: crowdsec <info@crowdsec.net>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: httpx==0.27.0
|
|
11
|
+
Requires-Dist: botocore
|
|
12
|
+
Requires-Dist: pydantic[email,timezone]<3.0.0,>=2.5.0
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# crowdsec_tracker_api
|
|
16
|
+
|
|
17
|
+
**crowdsec_tracker_api** is a Python SDK for the [CrowdSec Tracker API](https://docs.crowdsec.net/u/tracker_api/intro/).
|
|
18
|
+
This library enables you to manage CrowdSec Live Exploit Tracker resources such as CVEs data, IPs from a specific CVE and manage integrations.
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install crowdsec_tracker_api
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
You can follow this [documentation](https://docs.crowdsec.net/u/tracker_api/intro/) to see the basic usage of the SDK.
|
|
29
|
+
|
|
30
|
+
## Documentation
|
|
31
|
+
You can access the full usage documentation [here](https://github.com/crowdsecurity/crowdsec-tracker-api-sdk-python/tree/main/doc).
|
|
32
|
+
|
|
33
|
+
## Contributing
|
|
34
|
+
|
|
35
|
+
This SDK is auto-generated from the API specification. You can open an issue on this repository if you find a bug or if you want to add a feature.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# crowdsec_tracker_api
|
|
2
|
+
|
|
3
|
+
**crowdsec_tracker_api** is a Python SDK for the [CrowdSec Tracker API](https://docs.crowdsec.net/u/tracker_api/intro/).
|
|
4
|
+
This library enables you to manage CrowdSec Live Exploit Tracker resources such as CVEs data, IPs from a specific CVE and manage integrations.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install crowdsec_tracker_api
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
You can follow this [documentation](https://docs.crowdsec.net/u/tracker_api/intro/) to see the basic usage of the SDK.
|
|
15
|
+
|
|
16
|
+
## Documentation
|
|
17
|
+
You can access the full usage documentation [here](https://github.com/crowdsecurity/crowdsec-tracker-api-sdk-python/tree/main/doc).
|
|
18
|
+
|
|
19
|
+
## Contributing
|
|
20
|
+
|
|
21
|
+
This SDK is auto-generated from the API specification. You can open an issue on this repository if you find a bug or if you want to add a feature.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from .models import *
|
|
3
|
+
from .base_model import Page
|
|
4
|
+
from .services.integrations import Integrations
|
|
5
|
+
from .services.cves import Cves
|
|
6
|
+
from .http_client import ApiKeyAuth
|
|
7
|
+
|
|
8
|
+
class Server(Enum):
|
|
9
|
+
production_server = 'https://admin.api.crowdsec.net/v1'
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'Integrations',
|
|
13
|
+
'Cves',
|
|
14
|
+
'ApiKeyCredentials',
|
|
15
|
+
'BasicAuthCredentials',
|
|
16
|
+
'BlocklistSubscription',
|
|
17
|
+
'CVESubscription',
|
|
18
|
+
'HTTPValidationError',
|
|
19
|
+
'IntegrationCreateRequest',
|
|
20
|
+
'IntegrationCreateResponse',
|
|
21
|
+
'IntegrationGetResponse',
|
|
22
|
+
'IntegrationGetResponsePage',
|
|
23
|
+
'IntegrationType',
|
|
24
|
+
'IntegrationUpdateRequest',
|
|
25
|
+
'IntegrationUpdateResponse',
|
|
26
|
+
'Links',
|
|
27
|
+
'OutputFormat',
|
|
28
|
+
'Stats',
|
|
29
|
+
'ValidationError',
|
|
30
|
+
'AffectedComponent',
|
|
31
|
+
'AllowlistSubscription',
|
|
32
|
+
'AttackDetail',
|
|
33
|
+
'Behavior',
|
|
34
|
+
'CVEEvent',
|
|
35
|
+
'CVEResponseBase',
|
|
36
|
+
'CVEsubscription',
|
|
37
|
+
'CWE',
|
|
38
|
+
'Classification',
|
|
39
|
+
'Classifications',
|
|
40
|
+
'EntityType',
|
|
41
|
+
'GetCVEIPsResponsePage',
|
|
42
|
+
'GetCVEResponse',
|
|
43
|
+
'GetCVESubscribedIntegrationsResponsePage',
|
|
44
|
+
'GetCVEsResponsePage',
|
|
45
|
+
'GetCVEsSortBy',
|
|
46
|
+
'GetCVEsSortOrder',
|
|
47
|
+
'History',
|
|
48
|
+
'IPItem',
|
|
49
|
+
'IntegrationResponse',
|
|
50
|
+
'Location',
|
|
51
|
+
'MitreTechnique',
|
|
52
|
+
'Reference',
|
|
53
|
+
'ScoreBreakdown',
|
|
54
|
+
'Scores',
|
|
55
|
+
'SubscribeCVEIntegrationRequest',
|
|
56
|
+
'ApiKeyAuth',
|
|
57
|
+
'Server',
|
|
58
|
+
'Page'
|
|
59
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from urllib.parse import urlparse
|
|
2
|
+
from pydantic import BaseModel, ConfigDict, PrivateAttr, RootModel
|
|
3
|
+
from typing import Generic, Sequence, Optional, TypeVar, Any
|
|
4
|
+
from httpx import Auth
|
|
5
|
+
from .http_client import HttpClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseModelSdk(BaseModel):
|
|
9
|
+
model_config = ConfigDict(
|
|
10
|
+
extra="ignore",
|
|
11
|
+
)
|
|
12
|
+
_client: Optional["Service"] = PrivateAttr(default=None)
|
|
13
|
+
|
|
14
|
+
def __init__(self, /, _client: "Service" = None, **data):
|
|
15
|
+
super().__init__(**data)
|
|
16
|
+
self._client = _client
|
|
17
|
+
|
|
18
|
+
def next(self, client: "Service" = None) -> Optional["BaseModelSdk"]:
|
|
19
|
+
return (client if client is not None else self._client).next_page(self)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RootModelSdk(RootModel):
|
|
23
|
+
def __getattr__(self, item: str) -> Any:
|
|
24
|
+
return getattr(self.root, item)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
T = TypeVar("T")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Page(BaseModelSdk, Generic[T]):
|
|
31
|
+
items: Sequence[T]
|
|
32
|
+
total: Optional[int]
|
|
33
|
+
page: Optional[int]
|
|
34
|
+
size: Optional[int]
|
|
35
|
+
pages: Optional[int] = None
|
|
36
|
+
links: Optional[dict] = None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Service:
|
|
40
|
+
def __init__(self, base_url: str, auth: Auth) -> None:
|
|
41
|
+
self.http_client = HttpClient(base_url=base_url, auth=auth)
|
|
42
|
+
|
|
43
|
+
def next_page(self, page: BaseModelSdk) -> Optional[BaseModelSdk]:
|
|
44
|
+
if not hasattr(page, "links") or not page.links:
|
|
45
|
+
raise ValueError(
|
|
46
|
+
"No links found in the response, this is not a paginated response."
|
|
47
|
+
)
|
|
48
|
+
if page.links.next:
|
|
49
|
+
# links are relative to host not to full base url. We need to pass a full formatted url here
|
|
50
|
+
parsed_url = urlparse(self.http_client.base_url)
|
|
51
|
+
response = self.http_client.get(
|
|
52
|
+
f"{parsed_url.scheme}://{parsed_url.netloc}{page.links.next}",
|
|
53
|
+
path_params=None,
|
|
54
|
+
params=None,
|
|
55
|
+
headers=None,
|
|
56
|
+
)
|
|
57
|
+
return page.__class__(_client=self, **response.json())
|
|
58
|
+
return None
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from urllib.parse import quote, urlparse
|
|
2
|
+
|
|
3
|
+
from botocore.auth import SigV4Auth
|
|
4
|
+
from botocore.awsrequest import AWSRequest
|
|
5
|
+
import botocore.session
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AWSSignV4Auth(httpx.Auth):
|
|
11
|
+
def __init__(self, aws_region="eu-west-1") -> None:
|
|
12
|
+
self.aws_region = aws_region
|
|
13
|
+
|
|
14
|
+
def auth_flow(self, request):
|
|
15
|
+
request = self.sign_request(request)
|
|
16
|
+
yield request
|
|
17
|
+
|
|
18
|
+
def sign_request(self, request: httpx.Request) -> httpx.Request:
|
|
19
|
+
"""Signs an httpx request with AWS Signature Version 4."""
|
|
20
|
+
|
|
21
|
+
session = botocore.session.get_session()
|
|
22
|
+
credentials = session.get_credentials()
|
|
23
|
+
aws_request = AWSRequest(
|
|
24
|
+
method=request.method.upper(), url=str(request.url), data=request.content
|
|
25
|
+
)
|
|
26
|
+
region = self.aws_region
|
|
27
|
+
service = "execute-api"
|
|
28
|
+
|
|
29
|
+
# Sign the request
|
|
30
|
+
SigV4Auth(credentials, service, region).add_auth(aws_request)
|
|
31
|
+
|
|
32
|
+
# Update the httpx request headers with the signed headers
|
|
33
|
+
request.headers.update(dict(aws_request.headers))
|
|
34
|
+
|
|
35
|
+
return request
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ApiKeyAuth(httpx.Auth):
|
|
39
|
+
def __init__(self, api_key: str) -> None:
|
|
40
|
+
self.api_key = api_key
|
|
41
|
+
|
|
42
|
+
def auth_flow(self, request):
|
|
43
|
+
request.headers["x-api-key"] = self.api_key
|
|
44
|
+
yield request
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class HttpClient:
|
|
48
|
+
def __init__(self, base_url: str, auth: httpx.Auth, aws_region="eu-west-1") -> None:
|
|
49
|
+
self.aws_region = aws_region
|
|
50
|
+
self.base_url = base_url
|
|
51
|
+
self.auth = auth
|
|
52
|
+
self.client = httpx.Client(headers={"Accept-Encoding": "gzip"})
|
|
53
|
+
self.timeout = 30
|
|
54
|
+
|
|
55
|
+
def _replace_path_params(self, url: str, path_params: dict):
|
|
56
|
+
if path_params:
|
|
57
|
+
for param, value in path_params.items():
|
|
58
|
+
if not value:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
f"Parameter {param} is required, cannot be empty or blank."
|
|
61
|
+
)
|
|
62
|
+
url = url.replace(f"{{{param}}}", quote(str(value)))
|
|
63
|
+
return url
|
|
64
|
+
|
|
65
|
+
def _normalize_url(self, url: str):
|
|
66
|
+
self.base_url = self.base_url.rstrip("/")
|
|
67
|
+
parsed_url = urlparse(url)
|
|
68
|
+
if not parsed_url.netloc:
|
|
69
|
+
return f"{self.base_url}{url}"
|
|
70
|
+
return url
|
|
71
|
+
|
|
72
|
+
def get(
|
|
73
|
+
self,
|
|
74
|
+
url: str,
|
|
75
|
+
path_params: dict = None,
|
|
76
|
+
params: dict = None,
|
|
77
|
+
headers: dict = None,
|
|
78
|
+
):
|
|
79
|
+
url = self._replace_path_params(
|
|
80
|
+
url=self._normalize_url(url), path_params=path_params
|
|
81
|
+
)
|
|
82
|
+
response = self.client.get(
|
|
83
|
+
url=url,
|
|
84
|
+
params=params,
|
|
85
|
+
headers=headers,
|
|
86
|
+
auth=self.auth,
|
|
87
|
+
timeout=self.timeout,
|
|
88
|
+
)
|
|
89
|
+
response.raise_for_status()
|
|
90
|
+
return response
|
|
91
|
+
|
|
92
|
+
def post(
|
|
93
|
+
self, url: str, path_params: dict, params: dict, headers: dict, json: dict
|
|
94
|
+
):
|
|
95
|
+
url = self._replace_path_params(
|
|
96
|
+
url=self._normalize_url(url), path_params=path_params
|
|
97
|
+
)
|
|
98
|
+
response = self.client.post(
|
|
99
|
+
url=url,
|
|
100
|
+
params=params,
|
|
101
|
+
headers=headers,
|
|
102
|
+
json=json,
|
|
103
|
+
auth=self.auth,
|
|
104
|
+
timeout=self.timeout,
|
|
105
|
+
)
|
|
106
|
+
response.raise_for_status()
|
|
107
|
+
return response
|
|
108
|
+
|
|
109
|
+
def put(self, url: str, path_params: dict, params: dict, headers: dict, json: dict):
|
|
110
|
+
url = self._replace_path_params(
|
|
111
|
+
url=self._normalize_url(url), path_params=path_params
|
|
112
|
+
)
|
|
113
|
+
response = self.client.put(
|
|
114
|
+
url=url,
|
|
115
|
+
params=params,
|
|
116
|
+
headers=headers,
|
|
117
|
+
json=json,
|
|
118
|
+
auth=self.auth,
|
|
119
|
+
timeout=self.timeout,
|
|
120
|
+
)
|
|
121
|
+
response.raise_for_status()
|
|
122
|
+
return response
|
|
123
|
+
|
|
124
|
+
def patch(
|
|
125
|
+
self, url: str, path_params: dict, params: dict, headers: dict, json: dict
|
|
126
|
+
):
|
|
127
|
+
url = self._replace_path_params(
|
|
128
|
+
url=self._normalize_url(url), path_params=path_params
|
|
129
|
+
)
|
|
130
|
+
response = self.client.patch(
|
|
131
|
+
url=url,
|
|
132
|
+
params=params,
|
|
133
|
+
headers=headers,
|
|
134
|
+
json=json,
|
|
135
|
+
auth=self.auth,
|
|
136
|
+
timeout=self.timeout,
|
|
137
|
+
)
|
|
138
|
+
response.raise_for_status()
|
|
139
|
+
return response
|
|
140
|
+
|
|
141
|
+
def delete(self, url: str, path_params: dict, params: dict, headers: dict):
|
|
142
|
+
url = self._replace_path_params(
|
|
143
|
+
url=self._normalize_url(url), path_params=path_params
|
|
144
|
+
)
|
|
145
|
+
response = self.client.delete(
|
|
146
|
+
url=url,
|
|
147
|
+
params=params,
|
|
148
|
+
headers=headers,
|
|
149
|
+
auth=self.auth,
|
|
150
|
+
timeout=self.timeout,
|
|
151
|
+
)
|
|
152
|
+
response.raise_for_status()
|
|
153
|
+
return response
|