From c6f9fdc0d42ebbc433036564a5438f5c97a03172 Mon Sep 17 00:00:00 2001 From: mazzma12 <23187108+mazzma12@users.noreply.github.com> Date: Mon, 10 Aug 2026 09:26:16 +0000 Subject: [PATCH] Update python SDK v0.18.5 --- crowdsec_service_api/__init__.py | 20 +- .../__pycache__/__init__.cpython-311.pyc | Bin 5024 -> 5414 bytes .../__pycache__/base_model.cpython-311.pyc | Bin 4378 -> 4370 bytes .../__pycache__/http_client.cpython-311.pyc | Bin 7768 -> 7760 bytes .../__pycache__/models.cpython-311.pyc | Bin 158635 -> 170968 bytes crowdsec_service_api/models.py | 543 ++++- .../__pycache__/__init__.cpython-311.pyc | Bin 207 -> 199 bytes .../__pycache__/allowlists.cpython-311.pyc | Bin 12917 -> 12909 bytes .../__pycache__/blocklists.cpython-311.pyc | Bin 15121 -> 15113 bytes .../services/__pycache__/cves.cpython-311.pyc | Bin 12321 -> 12356 bytes .../__pycache__/decisions.cpython-311.pyc | Bin 4338 -> 7214 bytes .../__pycache__/engines.cpython-311.pyc | Bin 0 -> 2498 bytes .../__pycache__/fingerprints.cpython-311.pyc | Bin 12310 -> 12345 bytes .../services/__pycache__/hub.cpython-311.pyc | Bin 3277 -> 3269 bytes .../services/__pycache__/info.cpython-311.pyc | Bin 2035 -> 2027 bytes .../__pycache__/integrations.cpython-311.pyc | Bin 8075 -> 8067 bytes .../__pycache__/metrics.cpython-311.pyc | Bin 2607 -> 2599 bytes .../__pycache__/products.cpython-311.pyc | Bin 3782 -> 3774 bytes .../tracker_events.cpython-311.pyc | Bin 2914 -> 2906 bytes .../__pycache__/tracker_tags.cpython-311.pyc | Bin 5698 -> 5690 bytes .../__pycache__/vendors.cpython-311.pyc | Bin 10351 -> 10343 bytes crowdsec_service_api/services/allowlists.py | 2 +- crowdsec_service_api/services/blocklists.py | 2 +- crowdsec_service_api/services/cves.py | 3 +- crowdsec_service_api/services/decisions.py | 80 +- crowdsec_service_api/services/engines.py | 37 + crowdsec_service_api/services/fingerprints.py | 3 +- crowdsec_service_api/services/hub.py | 2 +- crowdsec_service_api/services/info.py | 2 +- crowdsec_service_api/services/integrations.py | 2 +- crowdsec_service_api/services/metrics.py | 2 +- crowdsec_service_api/services/products.py | 2 +- .../services/tracker_events.py | 2 +- crowdsec_service_api/services/tracker_tags.py | 2 +- crowdsec_service_api/services/vendors.py | 2 +- doc/Blocklists.md | 9 +- doc/Cves.md | 6 +- doc/Decisions.md | 152 +- doc/Engines.md | 45 + doc/Fingerprints.md | 6 +- doc/Models.md | 211 +- doc/README.md | 36 +- public-openapi.json | 2107 ++++++++++++----- pyproject.toml | 2 +- uv.lock | 38 +- 45 files changed, 2578 insertions(+), 740 deletions(-) create mode 100644 crowdsec_service_api/services/__pycache__/engines.cpython-311.pyc create mode 100644 crowdsec_service_api/services/engines.py create mode 100644 doc/Engines.md diff --git a/crowdsec_service_api/__init__.py b/crowdsec_service_api/__init__.py index f2c57ce..ff34502 100644 --- a/crowdsec_service_api/__init__.py +++ b/crowdsec_service_api/__init__.py @@ -5,6 +5,7 @@ from .services.blocklists import Blocklists from .services.integrations import Integrations from .services.decisions import Decisions +from .services.engines import Engines from .services.info import Info from .services.metrics import Metrics from .services.hub import Hub @@ -24,6 +25,7 @@ class Server(Enum): 'Blocklists', 'Integrations', 'Decisions', + 'Engines', 'Info', 'Metrics', 'Hub', @@ -33,6 +35,10 @@ class Server(Enum): 'TrackerTags', 'Fingerprints', 'TrackerEvents', + 'AggregatedDecisionItem', + 'AggregatedDecisionsGetResponse', + 'AggregatedDecisionsGetResponsePage', + 'AggregatedDecisionsSortBy', 'AllowlistCreateRequest', 'AllowlistCreateResponse', 'AllowlistGetItemsResponse', @@ -46,6 +52,7 @@ class Server(Enum): 'AllowlistSubscriberEntity', 'AllowlistSubscriberEntityPage', 'AllowlistSubscribersCount', + 'AllowlistSubscription', 'AllowlistSubscriptionRequest', 'AllowlistSubscriptionResponse', 'AllowlistUpdateRequest', @@ -67,7 +74,6 @@ class Server(Enum): 'BlocklistSubscriberEntity', 'BlocklistSubscriberEntityPage', 'BlocklistSubscribersCount', - 'BlocklistSubscription', 'BlocklistSubscriptionRequest', 'BlocklistSubscriptionResponse', 'BlocklistUpdateRequest', @@ -84,12 +90,16 @@ class Server(Enum): 'CtiScenario', 'DecisionCreateRequest', 'DecisionCreateResponse', + 'DecisionMachineState', + 'DecisionMachineStateEnum', 'DecisionResponse', 'DecisionTargetModel', 'DecisionTargetType', 'DecisionsGetResponsePage', 'DecisionsSortBy', 'DecisionsSortOrder', + 'EngineGetResponse', + 'EngineGetResponsePage', 'EntityType', 'FingerprintSubscription', 'GetRemediationMetricsResponse', @@ -104,6 +114,7 @@ class Server(Enum): 'IntegrationUpdateResponse', 'Links', 'MetricUnits', + 'OperatingSystem', 'OriginMetrics', 'OutputFormat', 'Permission', @@ -121,6 +132,8 @@ class Server(Enum): 'SubscriberEntityType', 'ValidationError', 'VendorSubscription', + 'capi__entities__lists_subscriber_entity__BlocklistSubscription', + 'src__api__v1__models__integrations__BlocklistSubscription', 'AppsecConfigIndex', 'AppsecRuleIndex', 'CollectionIndex', @@ -132,13 +145,13 @@ class Server(Enum): 'VersionDetail', 'AdjustmentScore', 'AffectedComponent', - 'AllowlistSubscription', 'AttackDetail', 'AttackerObjective', 'Behavior', + 'BlocklistSubscription', 'CVEEventOutput', 'CVEExploitationPhase', - 'CVEResponseBase', + 'CVEResponseDetailed', 'CVEsubscription', 'CWE', 'Classification', @@ -149,7 +162,6 @@ class Server(Enum): 'FacetBucket', 'FingerprintEventOutput', 'FingerprintRuleResponse', - 'FingerprintRuleSummary', 'FingerprintTimelineItem', 'GetCVEIPsResponsePage', 'GetCVEProtectRulesResponse', diff --git a/crowdsec_service_api/__pycache__/__init__.cpython-311.pyc b/crowdsec_service_api/__pycache__/__init__.cpython-311.pyc index 2db00239d7de4999aa3c5ce54bc923355cb316a5..91cf0a21c8f5fc1bc495d69f0f137ae0acc10464 100644 GIT binary patch delta 896 zcmZuv&1(}u6yHfoQ@c&g*Y-ng#A;j8qM-+cf)uJPwFf@~D)BIGcV9PSH@o4@Bod@3 zQZF8aGAF@?)sEzy&UGmCB z`7?4S6K@Le?=_4)&?VD>Am2AbV*4+a08PO>Cs*aFreJg5D4CwOlth$*CzxQsDRmha zobvK2w~(y@HeFRp<=0+gvNp%jqO_4ynWePo+u*7!<_U1>>`IMhsC?4@Qr^^ORH-Ua zKGdgD$+K6NT8b@#FqsRs{HT8mUJbkt$BkA)uveWfWG3WB?9tsA2dpp)_z7sVo1=ZSec!$N?)KbQ%jO%ysG=e=_s%D4 z*IdHCcbC7q3c8Mh))N$16z{1By_Nf4Y#!f1f&Ch9o(P-)>a~T7Z|Bc(4R>$#hipvy zki{{DzIT_ws83kTSwzqHN%+4OpOy1fN6vyGwZu$0rX4wGd+$Y8y`2A0H*mhT@Dn@s zYQ)GW3VAxnwm5+>NkbOrE0uFtyj;AWUpnmK{6TdI3sZlGZILY4;)8x#%=B)(cID>P zb;=WIfv)jtMV z2?^zA!+4v4CZ08&4P6SRddUp4EiejbE+!Mo!7Q+PDVxwZV8EePz>N}4fCU*zIn>3h z*?HI~Si5Am=Z-lv3$9&qDfnbW!J~4rJl`rma3arkO`zW JhcF-G1pvNZ5tje} delta 64 zcmbQFG)swlIWI340}wnd(b~wZ%&ZozpOK%Ns$W!^mzP?kA77qdl&znfpI4lpld79o NkO^gP_F+E83jlAr6*>R_ diff --git a/crowdsec_service_api/__pycache__/http_client.cpython-311.pyc b/crowdsec_service_api/__pycache__/http_client.cpython-311.pyc index 1bc5815900c09dcc5d59b8b7d1636dda5e5ee5f9..bc45bfbc039a75cf7910963f5003465bf8d946f3 100644 GIT binary patch delta 56 zcmca%bHRptIWI340}v?9tlY?bj!`Z|KO;XkRlle-FE6!7KfXM_C|kd%ELAtLAQMP$ J{?8aG0|5UK6FdL_ delta 64 zcmca$bHj#vIWI340}wnd(b~v;j!`W}KO;XkRlle-FE6!7KfXM_C|f@{Kd(4HCsjAG NAQQ^o{GBmU1^~M}7T*8> diff --git a/crowdsec_service_api/__pycache__/models.cpython-311.pyc b/crowdsec_service_api/__pycache__/models.cpython-311.pyc index 6ec2ace3e093ffc22a4a8f92ab2f5aa7fa9fef13..75a85e1c276bd196e880930036028c285c8e6e05 100644 GIT binary patch literal 170968 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 delta 40657 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 QN3HDQ2JLIj9ZmcH0e}F5KL7v# diff --git a/crowdsec_service_api/models.py b/crowdsec_service_api/models.py index d2625ae..f91111b 100644 --- a/crowdsec_service_api/models.py +++ b/crowdsec_service_api/models.py @@ -1,18 +1,39 @@ # generated by datamodel-codegen: # filename: -# timestamp: 2026-06-11T08:42:40+00:00 +# timestamp: 2026-08-10T09:26:09+00:00 from __future__ import annotations from datetime import datetime from enum import IntEnum, StrEnum -from typing import Annotated, Dict, List, Literal, Optional, Union +from typing import Annotated, Any, Dict, List, Literal, Optional, Union from pydantic import AnyUrl, ConfigDict, Field, RootModel from .base_model import BaseModelSdk, RootModelSdk +class Total(RootModelSdk[int]): + root: Annotated[int, Field(ge=0, title='Total')] + + +class Page(RootModelSdk[int]): + root: Annotated[int, Field(ge=1, title='Page')] + + +class Size(RootModelSdk[int]): + root: Annotated[int, Field(ge=1, title='Size')] + + +class Pages(RootModelSdk[int]): + root: Annotated[int, Field(ge=0, title='Pages')] + + +class AggregatedDecisionsSortBy(StrEnum): + FIRST_CREATED_AT = 'first_created_at' + FIRST_EXPIRATION = 'first_expiration' + + class AllowlistCreateRequest(BaseModelSdk): model_config = ConfigDict( extra='forbid', @@ -76,22 +97,6 @@ class AllowlistCreateResponse(BaseModelSdk): ] -class Total(RootModelSdk[int]): - root: Annotated[int, Field(ge=0, title='Total')] - - -class Page(RootModelSdk[int]): - root: Annotated[int, Field(ge=1, title='Page')] - - -class Size(RootModelSdk[int]): - root: Annotated[int, Field(ge=1, title='Size')] - - -class Pages(RootModelSdk[int]): - root: Annotated[int, Field(ge=0, title='Pages')] - - class AllowlistItemUpdateRequest(BaseModelSdk): model_config = ConfigDict( extra='forbid', @@ -129,6 +134,10 @@ class AllowlistScope(StrEnum): RANGE = 'range' +class AllowlistSubscription(BaseModelSdk): + id: Annotated[str, Field(title='Id')] + + class AllowlistSubscriptionResponse(BaseModelSdk): updated: Annotated[ Optional[List[str]], @@ -244,6 +253,13 @@ class BlocklistCreateRequest(BaseModelSdk): tags: Annotated[ Optional[List[str]], Field(description='Classification tags', title='Tags') ] = [] + store_full_content_in_s3: Annotated[ + Optional[bool], + Field( + description='Whether to store the full blocklist content in S3 or not', + title='Store Full Content In S3', + ), + ] = False class BlocklistDeleteIPsRequest(BaseModelSdk): @@ -266,13 +282,6 @@ class BlocklistSources(StrEnum): CUSTOM = 'custom' -class BlocklistSubscription(BaseModelSdk): - id: Annotated[str, Field(title='Id')] - remediation: Annotated[Optional[str], Field(title='Remediation')] = None - name: Annotated[str, Field(title='Name')] - label: Annotated[str, Field(title='Label')] - - class BlocklistSubscriptionResponse(BaseModelSdk): updated: Annotated[ Optional[List[str]], @@ -420,6 +429,12 @@ class DecisionCreateResponse(BaseModelSdk): ] +class DecisionMachineStateEnum(StrEnum): + PENDING_CREATE = 'PENDING_CREATE' + PENDING_DELETE = 'PENDING_DELETE' + APPLIED = 'APPLIED' + + class DecisionTargetType(StrEnum): ORG = 'org' TAG = 'tag' @@ -497,6 +512,12 @@ class MetricUnits(StrEnum): EVENT = 'event' +class OperatingSystem(BaseModelSdk): + name: Annotated[str, Field(title='Name')] + version: Annotated[str, Field(title='Version')] + family: Annotated[Optional[str], Field(title='Family')] = None + + class OutputFormat(StrEnum): PLAIN_TEXT = 'plain_text' F5 = 'f5' @@ -587,6 +608,18 @@ class VendorSubscription(BaseModelSdk): id: Annotated[str, Field(description='Vendor ID', title='Id')] +class CapiEntitiesListsSubscriberEntityBlocklistSubscription(BaseModelSdk): + id: Annotated[str, Field(title='Id')] + remediation: Annotated[Optional[str], Field(title='Remediation')] = None + + +class SrcApiV1ModelsIntegrationsBlocklistSubscription(BaseModelSdk): + id: Annotated[str, Field(title='Id')] + remediation: Annotated[Optional[str], Field(title='Remediation')] = None + name: Annotated[str, Field(title='Name')] + label: Annotated[str, Field(title='Label')] + + class VersionDetail(BaseModelSdk): deprecated: Annotated[ Optional[bool], @@ -629,10 +662,6 @@ class AffectedComponent(BaseModelSdk): ] = None -class AllowlistSubscription(BaseModelSdk): - id: Annotated[str, Field(title='Id')] - - class AttackDetail(BaseModelSdk): name: Annotated[str, Field(description='Attack detail name', title='Name')] label: Annotated[str, Field(description='Attack detail label', title='Label')] @@ -659,6 +688,11 @@ class Behavior(BaseModelSdk): ] +class BlocklistSubscription(BaseModelSdk): + id: Annotated[str, Field(title='Id')] + remediation: Annotated[Optional[str], Field(title='Remediation')] = None + + class CVEEventOutput(BaseModelSdk): name: Annotated[str, Field(title='Name')] date: Annotated[str, Field(title='Date')] @@ -670,10 +704,13 @@ class CVEEventOutput(BaseModelSdk): class CVEExploitationPhase(StrEnum): INSUFFICIENT_DATA = 'insufficient_data' EARLY_EXPLOITATION = 'early_exploitation' + RAPID_ESCALATION = 'rapid_escalation' + ACTIVE_EXPLOITATION = 'active_exploitation' + BACKGROUND_NOISE = 'background_noise' + LIMITED_EXPLOITATION = 'limited_exploitation' FRESH_AND_POPULAR = 'fresh_and_popular' TARGETED_EXPLOITATION = 'targeted_exploitation' MASS_EXPLOITATION = 'mass_exploitation' - BACKGROUND_NOISE = 'background_noise' UNPOPULAR = 'unpopular' WEARING_OUT = 'wearing_out' UNCLASSIFIED = 'unclassified' @@ -1380,7 +1417,7 @@ class BlocklistsUploadBlocklistContentQueryParameters(BaseModelSdk): Optional[datetime], Field( description='Blocklist expiration', - examples=['2026-06-11T08:40:13.370581+00:00'], + examples=['2026-08-03T13:27:52.522709+00:00'], title='Expiration', ), ] = None @@ -1587,6 +1624,12 @@ class IntegrationsGetIntegrationContentStreamPathParameters(BaseModelSdk): class DecisionsGetDecisionsQueryParameters(BaseModelSdk): + page: Annotated[ + Optional[int], Field(description='Page number', ge=1, title='Page') + ] = 1 + size: Annotated[ + Optional[int], Field(description='Page size', ge=1, le=100, title='Size') + ] = 50 instance_ids: Annotated[ Optional[List[str]], Field( @@ -1619,6 +1662,30 @@ class DecisionsGetDecisionsQueryParameters(BaseModelSdk): title='Ips', ), ] = [] + alert_ids: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by associated alert IDs', + examples=[['alert-abc123', 'alert-def456']], + title='Alert Ids', + ), + ] = [] + decision_ids: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by decision IDs', + examples=[['dec-abc123', 'dec-def456']], + title='Decision Ids', + ), + ] = [] + created_at_from: Annotated[ + Optional[datetime], + Field( + description='Filter decisions created after this date (inclusive)', + examples=['2023-01-01T00:00:00Z'], + title='Created At From', + ), + ] = None sort_by: Annotated[ Optional[DecisionsSortBy], Field( @@ -1635,18 +1702,119 @@ class DecisionsGetDecisionsQueryParameters(BaseModelSdk): title='Sort Order', ), ] = 'desc' + + +class DecisionsDeleteDecisionPathParameters(BaseModelSdk): + decision_id: Annotated[str, Field(title='Decision Id')] + + +class DecisionsGetAggregatedDecisionsQueryParameters(BaseModelSdk): page: Annotated[ Optional[int], Field(description='Page number', ge=1, title='Page') ] = 1 size: Annotated[ Optional[int], Field(description='Page size', ge=1, le=100, title='Size') ] = 50 + instance_ids: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by instance IDs', + examples=[['inst-abc123', 'inst-def456']], + title='Instance Ids', + ), + ] = [] + tag_ids: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by tag IDs', + examples=[['tag-001', 'tag-002']], + title='Tag Ids', + ), + ] = [] + remediation_types: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by remediation types', + examples=[['ban', 'captcha']], + title='Remediation Types', + ), + ] = [] + ips: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by IPs (only for IP decisions)', + examples=[['203.0.113.7', '198.51.100.42']], + title='Ips', + ), + ] = [] + alert_ids: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by associated alert IDs', + examples=[['alert-abc123', 'alert-def456']], + title='Alert Ids', + ), + ] = [] + decision_ids: Annotated[ + Optional[List[str]], + Field( + description='Filter decisions by decision IDs', + examples=[['dec-abc123', 'dec-def456']], + title='Decision Ids', + ), + ] = [] + created_at_from: Annotated[ + Optional[datetime], + Field( + description='Filter decisions created after this date (inclusive)', + examples=['2023-01-01T00:00:00Z'], + title='Created At From', + ), + ] = None + sort_by: Annotated[ + Optional[AggregatedDecisionsSortBy], + Field( + description='Field to sort by (e.g., created_at, duration)', + examples=['created_at'], + title='Sort By', + ), + ] = 'first_created_at' + sort_order: Annotated[ + Optional[DecisionsSortOrder], + Field( + description="Sort order: 'asc' for ascending, 'desc' for descending", + examples=['desc'], + title='Sort Order', + ), + ] = 'desc' -class DecisionsDeleteDecisionPathParameters(BaseModelSdk): +class DecisionsDeleteAggregatedDecisionsPathParameters(BaseModelSdk): + aggregated_id: Annotated[str, Field(title='Aggregated Id')] + + +class DecisionsDeleteAggregatedDecisionItemPathParameters(BaseModelSdk): + aggregated_id: Annotated[str, Field(title='Aggregated Id')] decision_id: Annotated[str, Field(title='Decision Id')] +class EnginesGetEnginesQueryParameters(BaseModelSdk): + tag: Annotated[ + Optional[List[str]], + Field( + description='List of tags associated with the engines (any of)', + examples=[['web-prod', 'linux']], + title='Tag', + ), + ] = None + page: Annotated[ + Optional[int], Field(description='Page number', ge=1, title='Page') + ] = 1 + size: Annotated[ + Optional[int], Field(description='Page size', ge=1, le=100, title='Size') + ] = 50 + + class MetricsGetMetricsRemediationQueryParameters(BaseModelSdk): start_date: Annotated[ Optional[datetime], @@ -1749,6 +1917,14 @@ class CvesGetCvesQueryParameters(BaseModelSdk): title='Exploitation Phase', ), ] = None + detailed: Annotated[ + Optional[bool], + Field( + description='Include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE in the list. Defaults to false to keep the response lightweight.', + examples=[True], + title='Detailed', + ), + ] = False page: Annotated[ Optional[int], Field(description='Page number', ge=1, title='Page') ] = 1 @@ -2177,6 +2353,14 @@ class FingerprintsGetFingerprintRulesQueryParameters(BaseModelSdk): title='Sort Order', ), ] = 'desc' + detailed: Annotated[ + Optional[bool], + Field( + description='Include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each fingerprint rule in the list. Defaults to false to keep the response lightweight.', + examples=[True], + title='Detailed', + ), + ] = False page: Annotated[ Optional[int], Field(description='Page number', ge=1, title='Page') ] = 1 @@ -2639,14 +2823,31 @@ class BlocklistSubscriptionRequest(BaseModelSdk): ) ids: Annotated[ Optional[List[str]], - Field(description='List of subscriber entity id', title='Ids'), + Field( + description='List of subscriber entity id. Must be empty for organization type subscription', + title='Ids', + ), ] = None entity_type: SubscriberEntityType remediation: Annotated[ - Optional[str], Field(description='Remediation', title='Remediation') + Optional[str], + Field( + description='Remediation, optional for firewall integration and required for other entity types', + title='Remediation', + ), ] = None +class DecisionMachineState(BaseModelSdk): + timestamp: Annotated[ + datetime, + Field(description='Date of when the state has been set', title='Timestamp'), + ] + state: Annotated[ + DecisionMachineStateEnum, Field(description='State of the decision') + ] + + class DecisionTargetModel(BaseModelSdk): type: Annotated[ DecisionTargetType, Field(description='Type of the decision target') @@ -2656,6 +2857,55 @@ class DecisionTargetModel(BaseModelSdk): ] +class EngineGetResponse(BaseModelSdk): + id: Annotated[str, Field(description='ID of the engine', title='Id')] + organization_id: Annotated[ + str, Field(description='ID of the owner organization', title='Organization Id') + ] + created_at: Annotated[ + datetime, Field(description='Time the engine was created', title='Created At') + ] + entity_type: Annotated[ + Optional[EntityType], Field(description='Type of the entity') + ] = 'engine' + blocklists: Annotated[ + Optional[List[CapiEntitiesListsSubscriberEntityBlocklistSubscription]], + Field(title='Blocklists'), + ] = [] + allowlists: Annotated[ + Optional[List[AllowlistSubscription]], Field(title='Allowlists') + ] = [] + version: Annotated[Optional[str], Field(title='Version')] = None + enrolled_at: Annotated[Optional[datetime], Field(title='Enrolled At')] = None + os: Optional[OperatingSystem] = None + meta: Annotated[Optional[Dict[str, Any]], Field(title='Meta')] = {} + last_pull: Annotated[ + Optional[datetime], + Field(description='Last time the engine pulled CAPI', title='Last Pull'), + ] = None + last_push: Annotated[ + Optional[datetime], + Field(description='Last time the engine pushed to CAPI', title='Last Push'), + ] = None + last_login: Annotated[ + Optional[datetime], + Field(description='Last time the engine logged in CAPI', title='Last Login'), + ] = None + tags: Annotated[ + Optional[List[str]], + Field(description='List of tags associated with the engine', title='Tags'), + ] = [] + + +class EngineGetResponsePage(BaseModelSdk): + items: Annotated[List[EngineGetResponse], Field(title='Items')] + total: Annotated[Optional[Total], Field(title='Total')] = None + page: Annotated[Optional[Page], Field(title='Page')] = None + size: Annotated[Optional[Size], Field(title='Size')] = None + pages: Annotated[Optional[Pages], Field(title='Pages')] = None + links: Links + + class HTTPValidationError(BaseModelSdk): detail: Annotated[Optional[List[ValidationError]], Field(title='Detail')] = None @@ -2731,7 +2981,7 @@ class IntegrationCreateResponse(BaseModelSdk): ), ] = None blocklists: Annotated[ - List[BlocklistSubscription], + List[SrcApiV1ModelsIntegrationsBlocklistSubscription], Field( description='Blocklists that are subscribed by the integration', title='Blocklists', @@ -2826,7 +3076,7 @@ class IntegrationGetResponse(BaseModelSdk): ), ] = None blocklists: Annotated[ - List[BlocklistSubscription], + List[SrcApiV1ModelsIntegrationsBlocklistSubscription], Field( description='Blocklists that are subscribed by the integration', title='Blocklists', @@ -2957,7 +3207,7 @@ class IntegrationUpdateResponse(BaseModelSdk): ), ] = None blocklists: Annotated[ - List[BlocklistSubscription], + List[SrcApiV1ModelsIntegrationsBlocklistSubscription], Field( description='Blocklists that are subscribed by the integration', title='Blocklists', @@ -3728,7 +3978,7 @@ class ScenarioIndex(BaseModelSdk): ] = None -class CVEResponseBase(BaseModelSdk): +class CVEResponseDetailed(BaseModelSdk): id: Annotated[str, Field(description='ID of the CVE', title='Id')] name: Annotated[str, Field(description='Name of the CVE', title='Name')] title: Annotated[str, Field(description='Title of the CVE', title='Title')] @@ -3808,6 +4058,29 @@ class CVEResponseBase(BaseModelSdk): description='Threat context (attacker/defender countries, industries, objectives)' ), ] = None + tags: Annotated[ + Optional[List[str]], + Field(description='Tags associated with the CVE', title='Tags'), + ] = None + references: Annotated[ + Optional[List[str]], + Field(description='List of references for the CVE', title='References'), + ] = None + description: Annotated[ + Optional[str], Field(description='Description of the CVE', title='Description') + ] = None + crowdsec_analysis: Annotated[ + Optional[str], + Field(description='CrowdSec analysis of the CVE', title='Crowdsec Analysis'), + ] = None + cwes: Annotated[ + Optional[List[CWE]], + Field(description='List of CWEs associated with the CVE', title='Cwes'), + ] = None + events: Annotated[ + Optional[List[CVEEventOutput]], + Field(description='List of events related to the CVE', title='Events'), + ] = None class FingerprintRuleResponse(BaseModelSdk): @@ -3892,61 +4165,6 @@ class FingerprintRuleResponse(BaseModelSdk): ] = None -class FingerprintRuleSummary(BaseModelSdk): - id: Annotated[str, Field(description='Fingerprint rule identifier', title='Id')] - name: Annotated[str, Field(description='Fingerprint rule name', title='Name')] - title: Annotated[str, Field(description='Fingerprint rule title', title='Title')] - affected_components: Annotated[ - List[AffectedComponent], - Field(description='List of affected components', title='Affected Components'), - ] - crowdsec_score: Annotated[ - int, - Field( - description='Live Exploit Tracker score for the fingerprint rule', - ge=0, - le=10, - title='Crowdsec Score', - ), - ] - opportunity_score: Annotated[ - Optional[int], - Field(description='Opportunity score', ge=0, le=5, title='Opportunity Score'), - ] = 0 - momentum_score: Annotated[ - Optional[int], - Field(description='Momentum score', ge=0, le=5, title='Momentum Score'), - ] = 0 - first_seen: Annotated[ - Optional[datetime], Field(description='First seen date', title='First Seen') - ] = None - last_seen: Annotated[ - Optional[datetime], Field(description='Last seen date', title='Last Seen') - ] = None - nb_ips: Annotated[ - int, Field(description='Number of unique IPs observed', ge=0, title='Nb Ips') - ] - rule_release_date: Annotated[ - Optional[datetime], - Field( - description='Release date of the fingerprint rule', - title='Rule Release Date', - ), - ] = None - exploitation_phase: Annotated[ - ExploitationPhase, Field(description='Current exploitation phase') - ] - adjustment_score: Annotated[ - Optional[AdjustmentScore], Field(description='Score adjustment details') - ] = None - threat_context: Annotated[ - Optional[ThreatContext], - Field( - description='Threat context (attacker/defender countries, industries, objectives)' - ), - ] = None - - class GetCVEResponse(BaseModelSdk): id: Annotated[str, Field(description='ID of the CVE', title='Id')] name: Annotated[str, Field(description='Name of the CVE', title='Name')] @@ -4062,7 +4280,7 @@ class GetCVESubscribedIntegrationsResponsePage(BaseModelSdk): class GetCVEsResponsePage(BaseModelSdk): - items: Annotated[List[CVEResponseBase], Field(title='Items')] + items: Annotated[List[CVEResponseDetailed], Field(title='Items')] total: Annotated[int, Field(ge=0, title='Total')] page: Annotated[int, Field(ge=1, title='Page')] size: Annotated[int, Field(ge=1, title='Size')] @@ -4071,7 +4289,7 @@ class GetCVEsResponsePage(BaseModelSdk): class GetFingerprintRulesResponsePage(BaseModelSdk): - items: Annotated[List[FingerprintRuleSummary], Field(title='Items')] + items: Annotated[List[FingerprintRuleResponse], Field(title='Items')] total: Annotated[int, Field(ge=0, title='Total')] page: Annotated[int, Field(ge=1, title='Page')] size: Annotated[int, Field(ge=1, title='Size')] @@ -4398,6 +4616,141 @@ class ProtectRule(BaseModelSdk): ] = None +class AggregatedDecisionItem(BaseModelSdk): + id: Annotated[ + str, Field(description='ID of the decision aggregated item', title='Id') + ] + first_created_at: Annotated[ + datetime, + Field( + description='Creation date of the first decision in the group', + title='First Created At', + ), + ] + last_created_at: Annotated[ + datetime, + Field( + description='Creation date of the last decision in the group', + title='Last Created At', + ), + ] + origin: Annotated[str, Field(description='Origin of the decision', title='Origin')] + scenario: Annotated[ + str, Field(description='Scenario of the decision', title='Scenario') + ] + min_duration: Annotated[ + str, + Field( + description='Min duration in the group of decisions', title='Min Duration' + ), + ] + max_duration: Annotated[ + str, + Field( + description='Max durations in the group of decisions', title='Max Duration' + ), + ] + first_expiration: Annotated[ + datetime, + Field( + description='First expiration date in the group of decisions', + title='First Expiration', + ), + ] + last_expiration: Annotated[ + datetime, + Field( + description='Last expiration date in the group of decisions', + title='Last Expiration', + ), + ] + count: Annotated[ + int, Field(description='Number of decisions in the group', title='Count') + ] + machines: Annotated[ + Dict[str, DecisionMachineState], + Field( + description='Machines object (the instance ID is the key) with the decision status and timestamp', + title='Machines', + ), + ] + target: Annotated[DecisionTargetModel, Field(description='Target of the decision')] + + +class AggregatedDecisionsGetResponse(BaseModelSdk): + id: Annotated[ + str, Field(description='ID of the decision aggregated item', title='Id') + ] + scope: Annotated[str, Field(description='Scope of the decision', title='Scope')] + type: Annotated[str, Field(description='Type of the decision', title='Type')] + value: Annotated[str, Field(description='Value of the decision', title='Value')] + country: Annotated[ + Optional[str], + Field(description='Country associated with the decision', title='Country'), + ] = None + as_name: Annotated[ + Optional[str], + Field(description='AS name associated with the decision', title='As Name'), + ] = None + as_num: Annotated[ + Optional[int], + Field(description='AS number associated with the decision', title='As Num'), + ] = None + city: Annotated[ + Optional[str], + Field(description='City associated with the decision', title='City'), + ] = None + latitude: Annotated[ + Optional[float], + Field(description='Latitude associated with the decision', title='Latitude'), + ] = None + longitude: Annotated[ + Optional[float], + Field(description='Longitude associated with the decision', title='Longitude'), + ] = None + first_created_at: Annotated[ + Optional[datetime], + Field( + description='Creation date of the first decision in the group', + title='First Created At', + ), + ] = None + last_created_at: Annotated[ + Optional[datetime], + Field( + description='Creation date of the last decision in the group', + title='Last Created At', + ), + ] = None + first_expiration: Annotated[ + Optional[datetime], + Field( + description='Expiration date of the first decision in the group', + title='First Expiration', + ), + ] = None + last_expiration: Annotated[ + Optional[datetime], + Field( + description='Expiration date of the last decision in the group', + title='Last Expiration', + ), + ] = None + decisions: Annotated[ + List[AggregatedDecisionItem], + Field(description='List of decisions in the group', title='Decisions'), + ] + + +class AggregatedDecisionsGetResponsePage(BaseModelSdk): + items: Annotated[List[AggregatedDecisionsGetResponse], Field(title='Items')] + total: Annotated[Optional[Total], Field(title='Total')] = None + page: Annotated[Optional[Page], Field(title='Page')] = None + size: Annotated[Optional[Size], Field(title='Size')] = None + pages: Annotated[Optional[Pages], Field(title='Pages')] = None + links: Links + + class AllowlistGetItemsResponse(BaseModelSdk): id: Annotated[ str, diff --git a/crowdsec_service_api/services/__pycache__/__init__.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/__init__.cpython-311.pyc index 03cc6cd2b678c4bb7fde146020819a3126108035..afcb7362e68de053b939c5c75b20d3e3481bdf33 100644 GIT binary patch delta 38 scmX@lc$|@YIWI340}v?9tenVg${01#QJAZ!ELAtLAXC4nY+_m_0KJk5=>Px# delta 46 wcmX@kc%G4aIWI340}wnd(VEC@%9u3KQCKKBKd(4HCsjAGAX6X0o*0)205BI0i~s-t diff --git a/crowdsec_service_api/services/__pycache__/allowlists.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/allowlists.cpython-311.pyc index 43c12d0fa29acc733d3ff5413d13fc4bbf224419..6390a5cbeb915cc795e521f8c026b16fc8ed2349 100644 GIT binary patch delta 68 zcmeyG@-~HgIWI340}vR_tlY>Q%gkP8pl4{IXF55bIY=%>KO;XkRlle-FE6!7KfXM_ VC|kd%ELAtLAQMP$e$L#d2LRLa7M%b9 delta 76 zcmaEx@->BfIWI340|=CAZRCz+W;fI`G_ue$n4HfXq?V$ek)NBYUsRfxms+GBU!Gr- Yt)HBqSDc@ds+(Al31x4-&)la60AeE<1poj5 diff --git a/crowdsec_service_api/services/__pycache__/blocklists.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/blocklists.cpython-311.pyc index d87cdb1bac7f4880ebeb44a85f3d6e4d5ffdd4cb..146436126565ea675efa6d9cf6dd39284695d926 100644 GIT binary patch delta 68 zcmbPO)>+2AoR^o20SF9dR&M0J%gkP8pl4{IXFB;kbC6t&enx(7s(w*vUS4XEetda; VQMP_jS*mVgK_-yioX)b!3;@%n7M=hA delta 76 zcmeAyn^?xZoR^o20R&35Hgex(W;fI`G_ue$nEakONG(M_BR@A)zo;}XFSSTNzC6Du YTR%BJuQ)#^RX4F96UyEk&$7x40A#iq1^@s6 diff --git a/crowdsec_service_api/services/__pycache__/cves.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/cves.cpython-311.pyc index 7d05d919d947b7bbc0e723946bb4f0ca957ad427..729611b7a79d002f172e2c85f49088db096ea544 100644 GIT binary patch delta 2202 zcma)8ZD?C%6uxhI^VxfoCe3G?v}uzzA6=7mYwX&(4c1~SI>$cHt+;lz@0*#LCgE*b zSJyhk!S+MT`a_U0&$+R*o&DGi_qp$L z?s?C1&bjAavUKLhGwK(rYLVdRx;{5KbVD5^jdrAP{P`P^q)XCOiAf`bN+UYjCbDGs z0|e^Ec8QYVvvThlUi zU6e;`|M5EDZs2y{7tlclg9i)_vxGm|t~vUiNNP=>K5d~5Am9Yw$fmwBbEOTm5q#WQ z#Hi-A7QvG?Q9s=EcGCd!;Y~=P2~ud#9Gf*B1l|Y|G0mnjKW%}O0<1|a>pTYota+^i z5Uv%%1vSqQYk{HytaaUe0}Vs8&}Hcy83`YkVsY_I&CkZ!-HEP!iIk|3k2SrVd_6zK zl6)qYV>~(bW}d&1xA?yK&nfP8@cO#_4 z59TKEFl5&8UUA>t*nAMJ{Rqz@Y}DfW#W#vu^jX{_UOH-dutVrmN_;i+q7oznuz8pq zDE+P6+F|3b)^^;e@?OZHEEn0iBFEis5w=CivC?7NkgjE|sxHh_E6u#2w43%A=@Iwr zQNup;=oPA?HK7&w67mQFHhH^xPdkQ4y7ZZ2QTJpUH)rnOB+ zS6oNQJEZ~l<)fj4F7ft1S$vEmJiA)jqk?^Rzars+LZS>O9c$%iFhL!WN{2!uPq zwQi2VGN$L9*b|(s>Xd0SpiPU`W8;P%U-5V0qw0tEB|lw^S$G5P!$4fiW~;)>#=`hq zHa|{bru2c6c5jH)>sKPN7zJ*+5yA6yhrw z61^9Rz!Vyz?Wwi=);DPuW9-nRxoDI;5}!sNIu4_E5TOMiM)+Z|6ssp~;%2NcEWeh| z!v_hzOuP?W4j>#t7(^IC7)E#z;TXbkgjWzwBAiAT6=&mN1I1-Vd=T$w%HeVbpsXCv zvUJ_Ui~1cNQlce&$XKO7AktpWRM&U z*E;XmqZOmZO_D2cJJ5N@nW&gGgGCCo-EQl>{!M@pdz|nRA-u_3FAU4%5M;dd<#NwT N_3mHWIPn0){TFW|)Or8_ delta 2178 zcma)6OH5l=5WUwtf1d4!vGIqGjSU9u$H$ICzL2CUphA>>6q5c>NojYe{ z-uSIe-kwxGR}?G3&qCLQ$ci#dn;n7@^5;*KkPpaB!pJz)$hbiZ^AsIjnlWiMW?@p} zW*i}!9k{8AE5OZFycW1z#U0G5)xlR{wwYSZdDS>>*IY~juFObU{Z(nac9T1RyMfnj za%pBs(mddB$^`$kc5&c)qM(X^YsRWIfIvOOc5E9fF*mf-2r(X!u~S4=H+jsI}#yZ!?*ldwC5i04LCHDX3Uc$&D;5u=^j=1 zkU5+v+F4r5W|HaL`2{^ytVw02^*99UF@#=#qD5!(Sty##D?s_O`LN_74M9HT^%WkN zzoUK?L%R_Y0IEgbk9Hrz0lqG`*bbq27~u$iC^rXBpxKWwfUs3hKgL(>UVhT*rD|c) z`r97)XYE~E@-N#%bPzI((&GiQvbx9Zd$pb3s_LMcqLgEoayq8)@;*nDo-Ukoj2K!g zP42^*MW^zXPS>0&J;d)jqkP?OLrKpT1WZ^Im(JhW>6i2s(*Ic!S(l4GkMi+ z%I1>rD$_5a{|bN5)kk0E;qJGW+@7D^-jc&-9Hu3L_>04LtLDSSl2N!!#MN}Gcf%Pi znFY5HPhdUJyWx(NWZ|%qK*xH=(G7op$tE0j;&7D|Vb-EqPF+W2f5|DlLZz%#A|~P5 zU+C$%V4!{cX6%Y8-k13L)}08rIeGvApWGwMvAK-4kYXqFkN7}*_0)(&2chzv|6M!$ MNBQ)xU4n$bzmi(Lp#T5? diff --git a/crowdsec_service_api/services/__pycache__/decisions.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/decisions.cpython-311.pyc index d705642e1226909db262b964ed97acbc6e614089..c067d49121281f91d3d5489fdda2d45175b4d77d 100644 GIT binary patch literal 7214 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 diff --git a/crowdsec_service_api/services/__pycache__/engines.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/engines.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3160a1fa9058ecbdb62a86468c712cf27720839 GIT binary patch literal 2498 zcmahKOKclObk_UvdTqx^?6hfVOIrGgOKZ1OAcPR2EotFEC^Sg0s9Lt3NwVpBZD!YL z6FDM>O1M&mI22lL5kes-JrsllXHFbVRjJlUkt%V@1-VL*P*1$KYp01wVLX2C&6_uG z-n`#_mrff5#__Lbran~&`2!c-p>~M1uYkBoD4~)|OvEu)D#y&2gtY9+WyMs=s;QPW zQv;qtRX09{SabDq!c3G6(+FkbZnB&*Q{}Xo4%fQdQ_h%~kWRS0N zNQ|5%GC@;Or|nZ&_Cl^gN#d5yP7uD z1i>Hep$Vq3I8%14>w7{`gXuI0e}yH+b(*@Wnnr_Mk<29cAS2=<-h|^Z|MA{QFw1|X zShQ)`@d|d;DU^8SGWA)h;IW`MeOxFuyg=v|;h|QIyF%kEsBv$!wC-BCA48ApwQ~Tz zlx~th0-`e(0WWtKWMRY3I+N66b%{#1W1EA9fd+%+(b-*Hvn)Syz6G`xr;inmzg&1R zFY_Fz;#mN~sDXq9aqWE$!>r>K9q$??QEkV~}Qh zI{_$k?MegJ&ja{-o~-EUYw_#x`}*KreQ;s;POf?Ck;3)nv26ddYw#l8wXj?)(>wW>PlU&$Lrc{WfAUKUcYND z5oDf-QH`o~;z;$x))}@r3(;!GtoZ1x-VM$elxd1xDY-SuEDxFkd0p4_#`>yr(+)t_s`SNaBS>|9A5X{JuVT=FA)KXv+U>U3-O-nLBAZ%7YGEZy@6 z!9!W;Hy#lH4XtgE?K>LD2ifiSvd`Vm9=e-7)au=}^!zX5zfHio+%O!=fiQrE{+CXA z4>U6)Fqg&v-1*?=3qM}?!TQOHm`#0z?*=pT67NSOkMcc$`Lno4VO#JZ29_lZ%Yqox zT%=Q$^-0ZkqaEG@qC)lq9#$^L>=TOX_<@kY!5nLkzl6+mr;n7F>q4WE_%p!uG522& zy~_Jg6hR6%egy!<-u<;>l6p~U6NI*_|zNu({Q2kps=G=D+!^uBr_>_czI=*;WQ64C~ggG4U|mdGwV1&>#N* DA-Q## literal 0 HcmV?d00001 diff --git a/crowdsec_service_api/services/__pycache__/fingerprints.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/fingerprints.cpython-311.pyc index 43a3794d6559f7bed7b0eeec2036e4ad35245093..276f8477d87b36d5a93606b33761264e90d7c9eb 100644 GIT binary patch delta 2287 zcma)6Z){Ul6u-B9ZC_ve+O_MSbz4Wrwr+2K+Wi4G1~?~U2^bN_pDcqg3hxezrLFgM zFko|hAknBnlA8b~elRgg@K20XKj;UeUoLU$9pUPBb!1Fu9JGcMhG-ZJIXYl#H9G$ZN0^U{=R zNBpO^347qN9Hsq>Jb-IP(Ss1c4bIUOoh4EOH0!{}BSN+duQCPijI4)XwN|euKE4JN z8i0ad_%#YW40-@EbO>7$3F>Qzyh!RQ&Lxo$sW0mgGy*{zmN%)SQE!HGgh;sTRhHl= zA<|UVVgFj#zeXgKNCZf=0Ey<;(Hx#yyBld*fobNSY$tnTeM7PJ>@V{~)4}+$%n1^w zv*|RU@##|;`f{9}Bt40268_n&^eG-^Yc1{TWu4>_$rRBGQZ{jv6eJ^gh8(diHxVH& zo8f(+Kj6)CikSQ^;nY2L+71Rj3YMaE~>+&t!sBxbz4t)TcO#8V!z+oNx-M)dH^V|FB z5a*aRbdxYw{?(j8Dj6NoiUMe8`q;8_|aEm@KBw`Tk`u|?^ZH+GC4#k3@xYJ z?D3aa3@)3_dVJ$07TndnJ!_1KQ80Gr5;;S&SJXq0Tcs!$8-r}gGl3`bBi>7<=u->B z6OI6pUe@KW!5i3S|CpmprI)?nZ)KnPeVVAu|0>Fo|D^f~gf;E~oI^z?`%dw(X|FB+ zMxfrbw5H$;sDROY9~(N5At$& zugO~K0t>Wmm9Amh$39xKfN!&o$a=3hV)Yl1i42zJqgTwwg^0{;*NuC|SpA5~AAvVB(~cWhUJCQd-fs6kg|-og{5IjmAc&ir=U7`~GaMx#%BWL0&8 zNQG`uvAK>~ex*c8S4wa^nLbKrmcr#VF`FV2|6Wt{Ik-Rq?8lBF{0B2@Uqr_^mk5Vu z01Z=a&`g!4omva~MVneD9nNIn7KDg&Bj2!v!&VO4IqcxDlf#P~_HfwCNN2ZoKNkAB9R@h0=FE7wh*rjngZIVCpZ~E= H!w~Hsd@6prn#O!@GrIY3q9( zATq}X48~|;auUEqq92Tgh#$1ZgfApS6ZMl2i7_Q6_{A@3QlsDSgZJFdwdK$7HT}JF z&OP^>-#zEtcm43i!|KPXst|k@hmXfUQ`59x6)GWreMCALUb3`dJkoJ8aea++qMdx^HO=GvJr9%V4m@r`XPF*d$6k%A6sA)ZSfE1h6T zW3gCdM)J_9lJQdVNU3O+3T!l6&acQ|N}od1x>1$N*`ur`nfWv9hzvLFensJZzC?7o zB*UJreyc4{mhA9y*G6iC@14_D2iFx??%xoU&njmfXYJRdk0e4q;s3_7Y zm4MgfyVM0)+W?%m`NIkBVT>Y-aH!c0W|lcMX1cfvU)l~(Pt|R|O+!QIGmMbppxKQv zl*bV^!#|EK(-SCeL73!FlPc_V<*ME2L(2@pHiRjJ_N2x%oKT*pL)EX9{g!AmPZC{4 zGDlmoa4Z5DH7H}k+BnPy`rw>bfh($8Zls6L)!~FlnZ^(?ggC+)43y48bbIx9=X1Y8C)NV#JjLmxU@CxAo(-g(O)4ok7#M^b0ly}S@xSVDKX6hl^RVVG9L|xt3vMZX zDEe&Gcf0#6GxOZK&k(%!b5XMzoA^u2PGgEhYsFNE;@R3dSMQxouZ~GgDwoWPrZmcf z(bgW5nys9DrBKT1d|noVW%_3I*I>Wp!MSWt7!Ex%(@ZGqtiUR)ts^gD-ZeXNihfw_ z3GcVajc!0}aEEl28Y#HGZi(K8$;gCH9JT#MycU^k3}{MB%aw>7evY`OR-5zRxkYCV zBZ=%2n8c$8==JJy^n(^A=`o$}(rC@QyQt@nWGf|OSEW*(&sL5ZIlf|AI7A{w2xI3t zoSBB%&H)I;9dL8dPk)0S2Omj49NX4diY(-BszE;V=KCDM|c`x7s76Y=Md%)_9EkyRLRy=V_fF-gEa{?s#jdZWSh(`1&r7-ti9A zZ9=z`K=@X8^o~DKcL;+*+`hU~h%OWxkvfTEsO}b45wsC{1yeq2_+Fs5ZsqT4dSs7< uj>E;os~$0YZY1bMNI^8&OW%g6rc~p{nIWI340}vR_tlY?5!Ngu>pl4{IXF9oyDM&6_ih6&C;i delta 76 zcmX>qc~+8pIWI340|=CAZRDQ%EVq~pl4{IXF55ZDM&6%KO;XkRlle-FE6!7KfXM_ VC|kd%ELAtLAQMP$zRx7W3IL(v6)OM$ delta 76 zcmaFO|CyhAIWI340|=CAZR8GRVmH(?G_ue$n4HcOq!y>2k)NBYUsRfxms+GBU!Gr- Yt)HBqSDc@ds+(Al31x4-&LqML04d@ZlK=n! diff --git a/crowdsec_service_api/services/__pycache__/integrations.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/integrations.cpython-311.pyc index 53c1b9a9ec792af93fa36c27f740d87492cd99cb..eb58cbb2538b99b6ed7677c8f475baa7a79217e2 100644 GIT binary patch delta 68 zcmeCSZ?@-N&dbZi00f3JD>rhhGqaZ&=own*nNGH34w8%0&&bbB)h{Z|%S$cNk1x+J V%GNI`OVv#*$OO`xH!|On1^{-D6&L^j delta 76 zcmZp+@3!Y&&dbZi00O028@bh)*$wp!jV$yGCR;KGsio;>nG>u73b%q>LwOsLfM;FGT)L00QhkifB*mh diff --git a/crowdsec_service_api/services/__pycache__/metrics.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/metrics.cpython-311.pyc index 3dd88f559c910ce5387a7850a10836d8cccbb337..074bd5d846a0675863eeb36c12e433a77c1d7674 100644 GIT binary patch delta 68 zcmZ24vRs6FIWI340}vR_tlY>wgNeP&K+n)Z&vf!CrXaaU{fzwFRQ;mTyu8#R{rK|y VqHO)5vQ*u~f=nR2S&5mK9RQV;6mmyHA#TIWI340}vR_tlY?bpNYN9K+n)Z&vf!frXaZ}{fzwFRQ;mTyu8#R{rK|y VqHO)5vQ*u~f=nR2Ig>e-8vwx772f~= delta 76 zcmdlddrX#lIWI340|=CAZREbs#BQi(Xk?*hF!>`>kXn*{Mt*Lpeo<*&UTTqke0hFR YwtjMcUU7a-s%~OICX~H7kvWzd08gbDM*si- diff --git a/crowdsec_service_api/services/__pycache__/tracker_events.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/tracker_events.cpython-311.pyc index 80c82367ed26d1b3aa8f4a8bc76f0c16126bf6b6..942f88e3889f53ea7ed866ccac0374a226feab52 100644 GIT binary patch delta 54 zcmaDPc1w(VIWI340}vR_tlY?bhKaq*K+n)Z&vf!VrXa?I$%f3rTt#K6x`_pu`bA}% I!%+5M#zSVh1OTK`6{r9J delta 76 zcmdm`b4Z7KIWI340|=CAZR9RtW;fI`G_ue$nB2x3q?V?ik)NBYUsRfxms+GBU!Gr- Yt)HBqSDc@ds+(Al31x5o#_S>l04-7&5C8xG diff --git a/crowdsec_service_api/services/__pycache__/vendors.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/vendors.cpython-311.pyc index d4f743a50282e1bb623c56cac65a0e44cf40b373..cc3f4b67083193a79ce21950bdc94d31bd1ce392 100644 GIT binary patch delta 68 zcmaDK@H~KfIWI340}vR_tlY?bjG4X6K+n)Z&vfz)<{-I9{fzwFRQ;mTyu8#R{rK|y VqHO)5vQ*u~f=nR2IeXk?*hF!=^^kXoXCMt*Lpeo<*&UTTqke0hFR YwtjMcUU7a-s%~OICX~I|gGF8i0D4{+kpKVy diff --git a/crowdsec_service_api/services/allowlists.py b/crowdsec_service_api/services/allowlists.py index 057a909..205511e 100644 --- a/crowdsec_service_api/services/allowlists.py +++ b/crowdsec_service_api/services/allowlists.py @@ -11,7 +11,7 @@ class Allowlists(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def list_allowlists( self, diff --git a/crowdsec_service_api/services/blocklists.py b/crowdsec_service_api/services/blocklists.py index ac2cad5..f61b947 100644 --- a/crowdsec_service_api/services/blocklists.py +++ b/crowdsec_service_api/services/blocklists.py @@ -11,7 +11,7 @@ class Blocklists(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_blocklists( self, diff --git a/crowdsec_service_api/services/cves.py b/crowdsec_service_api/services/cves.py index 6d90155..b78985f 100644 --- a/crowdsec_service_api/services/cves.py +++ b/crowdsec_service_api/services/cves.py @@ -11,7 +11,7 @@ class Cves(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_cves( self, @@ -19,6 +19,7 @@ def get_cves( sort_by: Optional[GetCVEsSortBy] = GetCVEsSortBy("rule_release_date"), sort_order: Optional[GetCVEsSortOrder] = GetCVEsSortOrder("desc"), exploitation_phase: Optional[CVEExploitationPhase] = None, + detailed: bool = False, page: int = 1, size: int = 50, )-> GetCVEsResponsePage: diff --git a/crowdsec_service_api/services/decisions.py b/crowdsec_service_api/services/decisions.py index 7468e0a..afea1ec 100644 --- a/crowdsec_service_api/services/decisions.py +++ b/crowdsec_service_api/services/decisions.py @@ -11,18 +11,21 @@ class Decisions(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_decisions( self, + page: int = 1, + size: int = 50, instance_ids: list[str] = [], tag_ids: list[str] = [], remediation_types: list[str] = [], ips: list[str] = [], + alert_ids: list[str] = [], + decision_ids: list[str] = [], + created_at_from: Optional[str] = None, sort_by: Optional[DecisionsSortBy] = DecisionsSortBy("created_at"), sort_order: Optional[DecisionsSortOrder] = DecisionsSortOrder("desc"), - page: int = 1, - size: int = 50, )-> DecisionsGetResponsePage: endpoint_url = "/decisions" loc = locals() @@ -80,4 +83,75 @@ def delete_decision( ) return None + + def get_aggregated_decisions( + self, + page: int = 1, + size: int = 50, + instance_ids: list[str] = [], + tag_ids: list[str] = [], + remediation_types: list[str] = [], + ips: list[str] = [], + alert_ids: list[str] = [], + decision_ids: list[str] = [], + created_at_from: Optional[str] = None, + sort_by: Optional[AggregatedDecisionsSortBy] = AggregatedDecisionsSortBy("first_created_at"), + sort_order: Optional[DecisionsSortOrder] = DecisionsSortOrder("desc"), + )-> AggregatedDecisionsGetResponsePage: + endpoint_url = "/decisions/aggregated" + loc = locals() + headers = {} + params = json.loads( + DecisionsGetAggregatedDecisionsQueryParameters(**loc).model_dump_json( + exclude_none=True + ) + ) + path_params = {} + + response = self.http_client.get( + url=endpoint_url, path_params=path_params, params=params, headers=headers + ) + + return AggregatedDecisionsGetResponsePage(_client=self, **response.json()) + + def delete_aggregated_decisions( + self, + aggregated_id: str, + ): + endpoint_url = "/decisions/aggregated/{aggregated_id}" + loc = locals() + headers = {} + params = {} + path_params = json.loads( + DecisionsDeleteAggregatedDecisionsPathParameters(**loc).model_dump_json( + exclude_none=True + ) + ) + + response = self.http_client.delete( + url=endpoint_url, path_params=path_params, params=params, headers=headers + ) + + return None + + def delete_aggregated_decision_item( + self, + aggregated_id: str, + decision_id: str, + ): + endpoint_url = "/decisions/aggregated/{aggregated_id}/items/{decision_id}" + loc = locals() + headers = {} + params = {} + path_params = json.loads( + DecisionsDeleteAggregatedDecisionItemPathParameters(**loc).model_dump_json( + exclude_none=True + ) + ) + + response = self.http_client.delete( + url=endpoint_url, path_params=path_params, params=params, headers=headers + ) + + return None \ No newline at end of file diff --git a/crowdsec_service_api/services/engines.py b/crowdsec_service_api/services/engines.py new file mode 100644 index 0000000..0109c5e --- /dev/null +++ b/crowdsec_service_api/services/engines.py @@ -0,0 +1,37 @@ +import json +from types import NoneType +from typing import Optional, Union, Annotated + +from ..models import * +from ..base_model import Page, Service +from pydantic import BaseModel, Field +from pydantic.fields import FieldInfo +from httpx import Auth +from ..http_client import HttpClient + +class Engines(Service): + def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") + + def get_engines( + self, + tag: Optional[list[str]] = None, + page: int = 1, + size: int = 50, + )-> EngineGetResponsePage: + endpoint_url = "/engines" + loc = locals() + headers = {} + params = json.loads( + EnginesGetEnginesQueryParameters(**loc).model_dump_json( + exclude_none=True + ) + ) + path_params = {} + + response = self.http_client.get( + url=endpoint_url, path_params=path_params, params=params, headers=headers + ) + + return EngineGetResponsePage(_client=self, **response.json()) + \ No newline at end of file diff --git a/crowdsec_service_api/services/fingerprints.py b/crowdsec_service_api/services/fingerprints.py index 1bc0b40..8fc69d5 100644 --- a/crowdsec_service_api/services/fingerprints.py +++ b/crowdsec_service_api/services/fingerprints.py @@ -11,13 +11,14 @@ class Fingerprints(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_fingerprint_rules( self, query: Optional[str] = None, sort_by: Optional[GetCVEsSortBy] = GetCVEsSortBy("rule_release_date"), sort_order: Optional[GetCVEsSortOrder] = GetCVEsSortOrder("desc"), + detailed: bool = False, page: int = 1, size: int = 50, )-> GetFingerprintRulesResponsePage: diff --git a/crowdsec_service_api/services/hub.py b/crowdsec_service_api/services/hub.py index 3a1546e..1372032 100644 --- a/crowdsec_service_api/services/hub.py +++ b/crowdsec_service_api/services/hub.py @@ -11,7 +11,7 @@ class Hub(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_index( self, diff --git a/crowdsec_service_api/services/info.py b/crowdsec_service_api/services/info.py index c5b2050..991c7b0 100644 --- a/crowdsec_service_api/services/info.py +++ b/crowdsec_service_api/services/info.py @@ -11,7 +11,7 @@ class Info(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_info( self, diff --git a/crowdsec_service_api/services/integrations.py b/crowdsec_service_api/services/integrations.py index 73f5656..acb0f75 100644 --- a/crowdsec_service_api/services/integrations.py +++ b/crowdsec_service_api/services/integrations.py @@ -11,7 +11,7 @@ class Integrations(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_integrations( self, diff --git a/crowdsec_service_api/services/metrics.py b/crowdsec_service_api/services/metrics.py index 322c7c5..d1c0f0e 100644 --- a/crowdsec_service_api/services/metrics.py +++ b/crowdsec_service_api/services/metrics.py @@ -11,7 +11,7 @@ class Metrics(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_metrics_remediation( self, diff --git a/crowdsec_service_api/services/products.py b/crowdsec_service_api/services/products.py index 5d173fc..310cbc2 100644 --- a/crowdsec_service_api/services/products.py +++ b/crowdsec_service_api/services/products.py @@ -11,7 +11,7 @@ class Products(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_products( self, diff --git a/crowdsec_service_api/services/tracker_events.py b/crowdsec_service_api/services/tracker_events.py index fa41f55..e0c03ea 100644 --- a/crowdsec_service_api/services/tracker_events.py +++ b/crowdsec_service_api/services/tracker_events.py @@ -11,7 +11,7 @@ class TrackerEvents(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_exploitation_phase_change_events( self, diff --git a/crowdsec_service_api/services/tracker_tags.py b/crowdsec_service_api/services/tracker_tags.py index 270d8f4..ffe6e51 100644 --- a/crowdsec_service_api/services/tracker_tags.py +++ b/crowdsec_service_api/services/tracker_tags.py @@ -11,7 +11,7 @@ class TrackerTags(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_tags( self, diff --git a/crowdsec_service_api/services/vendors.py b/crowdsec_service_api/services/vendors.py index dab3932..902772c 100644 --- a/crowdsec_service_api/services/vendors.py +++ b/crowdsec_service_api/services/vendors.py @@ -11,7 +11,7 @@ class Vendors(Service): def __init__(self, auth: Auth, base_url: str = "https://admin.api.crowdsec.net/v1") -> None: - super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/1.128.0") + super().__init__(base_url=base_url, auth=auth, user_agent="crowdsec_service_api/v0.18.5") def get_vendors( self, diff --git a/doc/Blocklists.md b/doc/Blocklists.md index 78168f1..01e7143 100644 --- a/doc/Blocklists.md +++ b/doc/Blocklists.md @@ -80,7 +80,7 @@ except HTTPStatusError as e: ### Errors: | Code | Description | | ---- | ----------- | -| 409 | Blocklist already exists | +| 409 | Already exists | | 422 | Validation Error | ### Usage @@ -99,6 +99,7 @@ request = BlocklistCreateRequest( description=None, references=None, tags=None, + store_full_content_in_s3=None, ) try: response = client.create_blocklist( @@ -178,7 +179,7 @@ except HTTPStatusError as e: ### Errors: | Code | Description | | ---- | ----------- | -| 404 | Blocklist not found | +| 404 | Not found | | 422 | Validation Error | ### Usage @@ -251,8 +252,8 @@ except HTTPStatusError as e: ### Errors: | Code | Description | | ---- | ----------- | -| 403 | Blocklist is read-only | -| 404 | Blocklist not found | +| 403 | Read-only | +| 404 | Not found | | 500 | Internal server error | | 422 | Validation Error | ### Usage diff --git a/doc/Cves.md b/doc/Cves.md index 9b8a39b..a0e497f 100644 --- a/doc/Cves.md +++ b/doc/Cves.md @@ -3,7 +3,7 @@ # Cves Methods | Method | Description | | ------ | ----------- | -| [get_cves](#get_cves) | Get a paginated list of CVEs that CrowdSec is tracking | +| [get_cves](#get_cves) | Get a paginated list of CVEs that CrowdSec is tracking. Pass detailed=true to also include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE; they are omitted by default to keep the list light. | | [get_cve](#get_cve) | Get information about a specific CVE ID | | [get_cve_protect_rules](#get_cve_protect_rules) | Get protection/detection rules associated with a specific CVE ID | | [download_cve_ips](#download_cve_ips) | Download the list of IPs exploiting a specific CVE ID in raw format | @@ -16,7 +16,7 @@ | [get_cve_timeline](#get_cve_timeline) | Get timeline data of occurrences for a specific CVE ID | ## **get_cves** -### Get a paginated list of CVEs that CrowdSec is tracking +### Get a paginated list of CVEs that CrowdSec is tracking. Pass detailed=true to also include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE; they are omitted by default to keep the list light. - Endpoint: `/cves` - Method: `GET` @@ -27,6 +27,7 @@ | sort_by | Optional[GetCVEsSortBy] | Field to sort by | False | GetCVEsSortBy("rule_release_date") | | sort_order | Optional[GetCVEsSortOrder] | Sort order: ascending or descending | False | GetCVEsSortOrder("desc") | | exploitation_phase | Optional[CVEExploitationPhase] | Filter by exploitation phase | False | None | +| detailed | bool | Include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE in the list. Defaults to false to keep the response lightweight. | False | False | | page | int | Page number | False | 1 | | size | int | Page size | False | 50 | ### Returns: @@ -51,6 +52,7 @@ try: sort_by=rule_release_date, sort_order=desc, exploitation_phase=None, + detailed=True, page=1, size=50, ) diff --git a/doc/Decisions.md b/doc/Decisions.md index d4b739b..1a9c134 100644 --- a/doc/Decisions.md +++ b/doc/Decisions.md @@ -6,6 +6,9 @@ | [get_decisions](#get_decisions) | Get decisions | | [create_decision](#create_decision) | Create a new decision. | | [delete_decision](#delete_decision) | Delete a decision by its UUID. | +| [get_aggregated_decisions](#get_aggregated_decisions) | None | +| [delete_aggregated_decisions](#delete_aggregated_decisions) | Delete one decision by ID | +| [delete_aggregated_decision_item](#delete_aggregated_decision_item) | Delete one decision by ID | ## **get_decisions** ### Get decisions @@ -15,21 +18,23 @@ ### Parameters: | Parameter | Type | Description | Required | Default | | --------- | ---- | ----------- | -------- | ------- | +| page | int | Page number | False | 1 | +| size | int | Page size | False | 50 | | instance_ids | list[str] | Filter decisions by instance IDs | False | [] | | tag_ids | list[str] | Filter decisions by tag IDs | False | [] | | remediation_types | list[str] | Filter decisions by remediation types | False | [] | | ips | list[str] | Filter decisions by IPs (only for IP decisions) | False | [] | +| alert_ids | list[str] | Filter decisions by associated alert IDs | False | [] | +| decision_ids | list[str] | Filter decisions by decision IDs | False | [] | +| created_at_from | Optional[str] | Filter decisions created after this date (inclusive) | False | None | | sort_by | Optional[DecisionsSortBy] | Field to sort by (e.g., created_at, duration) | False | DecisionsSortBy("created_at") | | sort_order | Optional[DecisionsSortOrder] | Sort order: 'asc' for ascending, 'desc' for descending | False | DecisionsSortOrder("desc") | -| page | int | Page number | False | 1 | -| size | int | Page size | False | 50 | ### Returns: [DecisionsGetResponsePage](./Models.md#decisionsgetresponsepage) ### Errors: | Code | Description | | ---- | ----------- | | 404 | Not found | -| 500 | Internal server error | | 422 | Validation Error | ### Usage @@ -43,14 +48,17 @@ auth = ApiKeyAuth(api_key='your_api_key') client = Decisions(auth=auth) try: response = client.get_decisions( + page=1, + size=50, instance_ids=['sample-item'], tag_ids=['sample-item'], remediation_types=['sample-item'], ips=['sample-item'], + alert_ids=['sample-item'], + decision_ids=['sample-item'], + created_at_from=None, sort_by=created_at, sort_order=desc, - page=1, - size=50, ) print(response) except HTTPStatusError as e: @@ -72,7 +80,7 @@ except HTTPStatusError as e: ### Errors: | Code | Description | | ---- | ----------- | -| 409 | Conflict: Decision value is in allowlists; cannot create decision. | +| 409 | Already exists | | 422 | Validation Error | ### Usage @@ -125,7 +133,9 @@ except HTTPStatusError as e: ### Errors: | Code | Description | | ---- | ----------- | -| 422 | Validation Error | +| 422 | Unprocessable entity | +| 404 | Not found | +| 500 | Internal server error | ### Usage ```python @@ -145,3 +155,131 @@ except HTTPStatusError as e: print(f"An error occurred: {e.response.status_code} - {e.response.text}") ``` + +## **get_aggregated_decisions** +### None +- Endpoint: `/decisions/aggregated` +- Method: `GET` + +### Parameters: +| Parameter | Type | Description | Required | Default | +| --------- | ---- | ----------- | -------- | ------- | +| page | int | Page number | False | 1 | +| size | int | Page size | False | 50 | +| instance_ids | list[str] | Filter decisions by instance IDs | False | [] | +| tag_ids | list[str] | Filter decisions by tag IDs | False | [] | +| remediation_types | list[str] | Filter decisions by remediation types | False | [] | +| ips | list[str] | Filter decisions by IPs (only for IP decisions) | False | [] | +| alert_ids | list[str] | Filter decisions by associated alert IDs | False | [] | +| decision_ids | list[str] | Filter decisions by decision IDs | False | [] | +| created_at_from | Optional[str] | Filter decisions created after this date (inclusive) | False | None | +| sort_by | Optional[AggregatedDecisionsSortBy] | Field to sort by (e.g., created_at, duration) | False | AggregatedDecisionsSortBy("first_created_at") | +| sort_order | Optional[DecisionsSortOrder] | Sort order: 'asc' for ascending, 'desc' for descending | False | DecisionsSortOrder("desc") | +### Returns: +[AggregatedDecisionsGetResponsePage](./Models.md#aggregateddecisionsgetresponsepage) +### Errors: +| Code | Description | +| ---- | ----------- | +| 404 | Not found | +| 422 | Validation Error | +### Usage + +```python +from crowdsec_service_api import ( + Decisions, + ApiKeyAuth, +) +from httpx import HTTPStatusError +auth = ApiKeyAuth(api_key='your_api_key') +client = Decisions(auth=auth) +try: + response = client.get_aggregated_decisions( + page=1, + size=50, + instance_ids=['sample-item'], + tag_ids=['sample-item'], + remediation_types=['sample-item'], + ips=['sample-item'], + alert_ids=['sample-item'], + decision_ids=['sample-item'], + created_at_from=None, + sort_by=first_created_at, + sort_order=desc, + ) + print(response) +except HTTPStatusError as e: + print(f"An error occurred: {e.response.status_code} - {e.response.text}") +``` + + +## **delete_aggregated_decisions** +### Delete one decision by ID +- Endpoint: `/decisions/aggregated/{aggregated_id}` +- Method: `DELETE` + +### Parameters: +| Parameter | Type | Description | Required | Default | +| --------- | ---- | ----------- | -------- | ------- | +| aggregated_id | str | | True | | +### Errors: +| Code | Description | +| ---- | ----------- | +| 422 | Unprocessable entity | +| 404 | Not found | +| 500 | Internal server error | +### Usage + +```python +from crowdsec_service_api import ( + Decisions, + ApiKeyAuth, +) +from httpx import HTTPStatusError +auth = ApiKeyAuth(api_key='your_api_key') +client = Decisions(auth=auth) +try: + response = client.delete_aggregated_decisions( + aggregated_id='aggregated_id', + ) + print(response) +except HTTPStatusError as e: + print(f"An error occurred: {e.response.status_code} - {e.response.text}") +``` + + +## **delete_aggregated_decision_item** +### Delete one decision by ID +- Endpoint: `/decisions/aggregated/{aggregated_id}/items/{decision_id}` +- Method: `DELETE` + +### Parameters: +| Parameter | Type | Description | Required | Default | +| --------- | ---- | ----------- | -------- | ------- | +| aggregated_id | str | | True | | +| decision_id | str | | True | | +### Errors: +| Code | Description | +| ---- | ----------- | +| 422 | Unprocessable entity | +| 404 | Not found | +| 500 | Internal server error | +### Usage + +```python +from crowdsec_service_api import ( + Decisions, + ApiKeyAuth, +) +from httpx import HTTPStatusError +auth = ApiKeyAuth(api_key='your_api_key') +client = Decisions(auth=auth) +try: + response = client.delete_aggregated_decision_item( + aggregated_id='aggregated_id', + decision_id='decision_id', + ) + print(response) +except HTTPStatusError as e: + print(f"An error occurred: {e.response.status_code} - {e.response.text}") +``` + diff --git a/doc/Engines.md b/doc/Engines.md new file mode 100644 index 0000000..bbd42f6 --- /dev/null +++ b/doc/Engines.md @@ -0,0 +1,45 @@ + + +# Engines Methods +| Method | Description | +| ------ | ----------- | +| [get_engines](#get_engines) | Get engines for an organization | + +## **get_engines** +### Get engines for an organization +- Endpoint: `/engines` +- Method: `GET` + +### Parameters: +| Parameter | Type | Description | Required | Default | +| --------- | ---- | ----------- | -------- | ------- | +| tag | Optional[list[str]] | List of tags associated with the engines (any of) | False | None | +| page | int | Page number | False | 1 | +| size | int | Page size | False | 50 | +### Returns: +[EngineGetResponsePage](./Models.md#enginegetresponsepage) +### Errors: +| Code | Description | +| ---- | ----------- | +| 422 | Validation Error | +### Usage + +```python +from crowdsec_service_api import ( + Engines, + ApiKeyAuth, +) +from httpx import HTTPStatusError +auth = ApiKeyAuth(api_key='your_api_key') +client = Engines(auth=auth) +try: + response = client.get_engines( + tag=None, + page=1, + size=50, + ) + print(response) +except HTTPStatusError as e: + print(f"An error occurred: {e.response.status_code} - {e.response.text}") +``` + diff --git a/doc/Fingerprints.md b/doc/Fingerprints.md index addb9a3..c804f4b 100644 --- a/doc/Fingerprints.md +++ b/doc/Fingerprints.md @@ -3,7 +3,7 @@ # Fingerprints Methods | Method | Description | | ------ | ----------- | -| [get_fingerprint_rules](#get_fingerprint_rules) | Get a paginated list of fingerprint rules | +| [get_fingerprint_rules](#get_fingerprint_rules) | Get a paginated list of fingerprint rules. Pass detailed=true to also include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each rule; they are omitted by default to keep the list light. | | [download_fingerprint_ips](#download_fingerprint_ips) | Download the list of IPs exploiting a specific fingerprint rule in raw format | | [get_fingerprint_ips_details](#get_fingerprint_ips_details) | Get detailed information about IPs exploiting a specific fingerprint rule | | [get_fingerprint_ips_details_stats](#get_fingerprint_ips_details_stats) | Get aggregated statistics about IPs exploiting a specific fingerprint rule | @@ -15,7 +15,7 @@ | [get_fingerprint_rule](#get_fingerprint_rule) | Get information about a specific fingerprint rule | ## **get_fingerprint_rules** -### Get a paginated list of fingerprint rules +### Get a paginated list of fingerprint rules. Pass detailed=true to also include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each rule; they are omitted by default to keep the list light. - Endpoint: `/fingerprints` - Method: `GET` @@ -25,6 +25,7 @@ | query | Optional[str] | Search query for fingerprint rules | False | None | | sort_by | Optional[GetCVEsSortBy] | Field to sort by | False | GetCVEsSortBy("rule_release_date") | | sort_order | Optional[GetCVEsSortOrder] | Sort order: ascending or descending | False | GetCVEsSortOrder("desc") | +| detailed | bool | Include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each fingerprint rule in the list. Defaults to false to keep the response lightweight. | False | False | | page | int | Page number | False | 1 | | size | int | Page size | False | 50 | ### Returns: @@ -48,6 +49,7 @@ try: query=None, sort_by=rule_release_date, sort_order=desc, + detailed=True, page=1, size=50, ) diff --git a/doc/Models.md b/doc/Models.md index b6ab9c0..a3c6cbc 100644 --- a/doc/Models.md +++ b/doc/Models.md @@ -1,5 +1,63 @@ +# **AggregatedDecisionItem** +## Required: +id, first_created_at, last_created_at, origin, scenario, min_duration, max_duration, first_expiration, last_expiration, count, machines, target +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | ID of the decision aggregated item || +| first_created_at | str | Creation date of the first decision in the group || +| last_created_at | str | Creation date of the last decision in the group || +| origin | str | Origin of the decision || +| scenario | str | Scenario of the decision || +| min_duration | str | Min duration in the group of decisions || +| max_duration | str | Max durations in the group of decisions || +| first_expiration | str | First expiration date in the group of decisions || +| last_expiration | str | Last expiration date in the group of decisions || +| count | int | Number of decisions in the group || +| machines | Machines | Machines object (the instance ID is the key) with the decision status and timestamp || +| target | DecisionTargetModel | None || + +# **AggregatedDecisionsGetResponse** +## Required: +id, scope, type, value, decisions +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | ID of the decision aggregated item || +| scope | str | Scope of the decision || +| type | str | Type of the decision || +| value | str | Value of the decision || +| country | Optional[str] | Country associated with the decision || +| as_name | Optional[str] | AS name associated with the decision || +| as_num | Optional[int] | AS number associated with the decision || +| city | Optional[str] | City associated with the decision || +| latitude | Optional[float] | Latitude associated with the decision || +| longitude | Optional[float] | Longitude associated with the decision || +| first_created_at | str | Creation date of the first decision in the group || +| last_created_at | str | Creation date of the last decision in the group || +| first_expiration | str | Expiration date of the first decision in the group || +| last_expiration | str | Expiration date of the last decision in the group || +| decisions | list[AggregatedDecisionItem] | List of decisions in the group || + +# **AggregatedDecisionsGetResponsePage** +## Required: +items, page, size, links +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| items | list[AggregatedDecisionsGetResponse] | None || +| total | Optional[int] | None || +| page | Optional[int] | None || +| size | Optional[int] | None || +| pages | Optional[int] | None || +| links | Links | None || + +# **AggregatedDecisionsSortBy** +## Enum: +FIRST_CREATED_AT, FIRST_EXPIRATION + # **AllowlistCreateRequest** ## Required: name @@ -154,6 +212,14 @@ entity_type, count | entity_type | SubscriberEntityType | None || | count | int | Subscriber entity count || +# **AllowlistSubscription** +## Required: +id +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | None || + # **AllowlistSubscriptionRequest** ## Required: entity_type @@ -273,6 +339,7 @@ name, description | description | str | Blocklist description || | references | list[str] | Useful references on the list's origins || | tags | list[str] | Classification tags || +| store_full_content_in_s3 | bool | Whether to store the full blocklist content in S3 or not || # **BlocklistDeleteIPsRequest** ## Required: @@ -372,26 +439,15 @@ entity_type, count | entity_type | SubscriberEntityType | None || | count | int | Subscriber entity count || -# **BlocklistSubscription** -## Required: -id, name, label -## Properties -| Property | Type | Description | Example | -|----------|------|-------------|---------| -| id | str | None || -| remediation | Optional[str] | None || -| name | str | None || -| label | str | None || - # **BlocklistSubscriptionRequest** ## Required: entity_type ## Properties | Property | Type | Description | Example | |----------|------|-------------|---------| -| ids | list[str] | List of subscriber entity id || +| ids | list[str] | List of subscriber entity id. Must be empty for organization type subscription || | entity_type | SubscriberEntityType | None || -| remediation | Optional[str] | Remediation || +| remediation | Optional[str] | Remediation, optional for firewall integration and required for other entity types || # **BlocklistSubscriptionResponse** ## Required: @@ -553,6 +609,19 @@ uuid |----------|------|-------------|---------| | uuid | str | UUID of the created decision || +# **DecisionMachineState** +## Required: +timestamp, state +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| timestamp | str | Date of when the state has been set || +| state | DecisionMachineStateEnum | None || + +# **DecisionMachineStateEnum** +## Enum: +PENDING_CREATE, PENDING_DELETE, APPLIED + # **DecisionResponse** ## Required: uuid, id, duration, origin, scenario, scope, type, value, target @@ -610,6 +679,40 @@ CREATED_AT, EXPIRE_AT ## Enum: ASC, DESC +# **EngineGetResponse** +## Required: +id, organization_id, created_at +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | ID of the engine || +| organization_id | str | ID of the owner organization || +| created_at | str | Time the engine was created || +| entity_type | EntityType | None || +| blocklists | list[BlocklistSubscription] | None || +| allowlists | list[AllowlistSubscription] | None || +| version | Optional[str] | None || +| enrolled_at | Optional[str] | None || +| os | OperatingSystem | None || +| meta | Meta | None || +| last_pull | str | Last time the engine pulled CAPI || +| last_push | str | Last time the engine pushed to CAPI || +| last_login | str | Last time the engine logged in CAPI || +| tags | list[str] | List of tags associated with the engine || + +# **EngineGetResponsePage** +## Required: +items, page, size, links +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| items | list[EngineGetResponse] | None || +| total | Optional[int] | None || +| page | Optional[int] | None || +| size | Optional[int] | None || +| pages | Optional[int] | None || +| links | Links | None || + # **EntityType** ## Enum: ORG, TAG, ENGINE, FIREWALL_INTEGRATION, REMEDIATION_COMPONENT_INTEGRATION, REMEDIATION_COMPONENT, LOG_PROCESSOR @@ -780,6 +883,16 @@ id, name, organization_id, created_at, updated_at, entity_type, output_format, b ## Enum: BYTE, PACKET, REQUEST, IP, LINE, EVENT +# **OperatingSystem** +## Required: +name, version +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| name | str | None || +| version | str | None || +| family | Optional[str] | None || + # **OriginMetrics** ## Required: origin, data @@ -925,6 +1038,26 @@ id |----------|------|-------------|---------| | id | str | Vendor ID || +# **capi__entities__lists_subscriber_entity__BlocklistSubscription** +## Required: +id +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | None || +| remediation | Optional[str] | None || + +# **src__api__v1__models__integrations__BlocklistSubscription** +## Required: +id, name, label +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | None || +| remediation | Optional[str] | None || +| name | str | None || +| label | str | None || + # **AppsecConfigIndex** ## Properties | Property | Type | Description | Example | @@ -1058,14 +1191,6 @@ digest | vendor | Optional[str] | Vendor of the affected component || | product | Optional[str] | Product name of the affected component || -# **AllowlistSubscription** -## Required: -id -## Properties -| Property | Type | Description | Example | -|----------|------|-------------|---------| -| id | str | None || - # **AttackDetail** ## Required: name, label, description @@ -1091,6 +1216,15 @@ name, label, description | label | str | Behavior label || | description | str | Behavior description || +# **BlocklistSubscription** +## Required: +id +## Properties +| Property | Type | Description | Example | +|----------|------|-------------|---------| +| id | str | None || +| remediation | Optional[str] | None || + # **CVEEventOutput** ## Required: name, date, description, label, sorting_priority @@ -1105,9 +1239,9 @@ name, date, description, label, sorting_priority # **CVEExploitationPhase** ## Enum: -INSUFFICIENT_DATA, EARLY_EXPLOITATION, FRESH_AND_POPULAR, TARGETED_EXPLOITATION, MASS_EXPLOITATION, BACKGROUND_NOISE, UNPOPULAR, WEARING_OUT, UNCLASSIFIED +INSUFFICIENT_DATA, EARLY_EXPLOITATION, RAPID_ESCALATION, ACTIVE_EXPLOITATION, BACKGROUND_NOISE, LIMITED_EXPLOITATION, FRESH_AND_POPULAR, TARGETED_EXPLOITATION, MASS_EXPLOITATION, UNPOPULAR, WEARING_OUT, UNCLASSIFIED -# **CVEResponseBase** +# **CVEResponseDetailed** ## Required: id, name, title, affected_components, crowdsec_score, nb_ips, published_date, has_public_exploit, exploitation_phase ## Properties @@ -1130,6 +1264,12 @@ id, name, title, affected_components, crowdsec_score, nb_ips, published_date, ha | exploitation_phase | ExploitationPhase | None || | adjustment_score | Optional[AdjustmentScore] | Score adjustments applied to the CVE score based on various factors || | threat_context | Optional[ThreatContext] | Threat context (attacker/defender countries, industries, objectives) || +| tags | list[str] | Tags associated with the CVE || +| references | list[str] | List of references for the CVE || +| description | Optional[str] | Description of the CVE || +| crowdsec_analysis | Optional[str] | CrowdSec analysis of the CVE || +| cwes | list[CWE] | List of CWEs associated with the CVE || +| events | list[CVEEventOutput] | List of events related to the CVE || # **CVEsubscription** ## Required: @@ -1249,27 +1389,6 @@ id, name, title, affected_components, crowdsec_score, nb_ips, exploitation_phase | crowdsec_analysis | Optional[str] | CrowdSec analysis for this fingerprint rule || | events | list[FingerprintEventOutput] | List of events related to the fingerprint rule || -# **FingerprintRuleSummary** -## Required: -id, name, title, affected_components, crowdsec_score, nb_ips, exploitation_phase -## Properties -| Property | Type | Description | Example | -|----------|------|-------------|---------| -| id | str | Fingerprint rule identifier || -| name | str | Fingerprint rule name || -| title | str | Fingerprint rule title || -| affected_components | list[AffectedComponent] | List of affected components || -| crowdsec_score | int | Live Exploit Tracker score for the fingerprint rule || -| opportunity_score | int | Opportunity score || -| momentum_score | int | Momentum score || -| first_seen | Optional[str] | First seen date || -| last_seen | Optional[str] | Last seen date || -| nb_ips | int | Number of unique IPs observed || -| rule_release_date | Optional[str] | Release date of the fingerprint rule || -| exploitation_phase | ExploitationPhase | None || -| adjustment_score | Optional[AdjustmentScore] | Score adjustment details || -| threat_context | Optional[ThreatContext] | Threat context (attacker/defender countries, industries, objectives) || - # **FingerprintTimelineItem** ## Required: timestamp, count @@ -1347,7 +1466,7 @@ items, total, page, size, pages, links ## Properties | Property | Type | Description | Example | |----------|------|-------------|---------| -| items | list[CVEResponseBase] | None || +| items | list[CVEResponseDetailed] | None || | total | int | None || | page | int | None || | size | int | None || @@ -1381,7 +1500,7 @@ items, total, page, size, pages, links ## Properties | Property | Type | Description | Example | |----------|------|-------------|---------| -| items | list[FingerprintRuleSummary] | None || +| items | list[FingerprintRuleResponse] | None || | total | int | None || | page | int | None || | size | int | None || diff --git a/doc/README.md b/doc/README.md index 445188f..5a2395a 100644 --- a/doc/README.md +++ b/doc/README.md @@ -19,6 +19,8 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [Decisions](./Decisions.md) +[Engines](./Engines.md) + [Info](./Info.md) [Metrics](./Metrics.md) @@ -39,6 +41,14 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ ## API Endpoint models +[AggregatedDecisionItem](./Models.md#aggregateddecisionitem) + +[AggregatedDecisionsGetResponse](./Models.md#aggregateddecisionsgetresponse) + +[AggregatedDecisionsGetResponsePage](./Models.md#aggregateddecisionsgetresponsepage) + +[AggregatedDecisionsSortBy](./Models.md#aggregateddecisionssortby) + [AllowlistCreateRequest](./Models.md#allowlistcreaterequest) [AllowlistCreateResponse](./Models.md#allowlistcreateresponse) @@ -65,6 +75,8 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [AllowlistSubscribersCount](./Models.md#allowlistsubscriberscount) +[AllowlistSubscription](./Models.md#allowlistsubscription) + [AllowlistSubscriptionRequest](./Models.md#allowlistsubscriptionrequest) [AllowlistSubscriptionResponse](./Models.md#allowlistsubscriptionresponse) @@ -107,8 +119,6 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [BlocklistSubscribersCount](./Models.md#blocklistsubscriberscount) -[BlocklistSubscription](./Models.md#blocklistsubscription) - [BlocklistSubscriptionRequest](./Models.md#blocklistsubscriptionrequest) [BlocklistSubscriptionResponse](./Models.md#blocklistsubscriptionresponse) @@ -141,6 +151,10 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [DecisionCreateResponse](./Models.md#decisioncreateresponse) +[DecisionMachineState](./Models.md#decisionmachinestate) + +[DecisionMachineStateEnum](./Models.md#decisionmachinestateenum) + [DecisionResponse](./Models.md#decisionresponse) [DecisionTargetModel](./Models.md#decisiontargetmodel) @@ -153,6 +167,10 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [DecisionsSortOrder](./Models.md#decisionssortorder) +[EngineGetResponse](./Models.md#enginegetresponse) + +[EngineGetResponsePage](./Models.md#enginegetresponsepage) + [EntityType](./Models.md#entitytype) [FingerprintSubscription](./Models.md#fingerprintsubscription) @@ -181,6 +199,8 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [MetricUnits](./Models.md#metricunits) +[OperatingSystem](./Models.md#operatingsystem) + [OriginMetrics](./Models.md#originmetrics) [OutputFormat](./Models.md#outputformat) @@ -215,6 +235,10 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [VendorSubscription](./Models.md#vendorsubscription) +[capi__entities__lists_subscriber_entity__BlocklistSubscription](./Models.md#capi__entities__lists_subscriber_entity__blocklistsubscription) + +[src__api__v1__models__integrations__BlocklistSubscription](./Models.md#src__api__v1__models__integrations__blocklistsubscription) + [AppsecConfigIndex](./Models.md#appsecconfigindex) [AppsecRuleIndex](./Models.md#appsecruleindex) @@ -237,19 +261,19 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [AffectedComponent](./Models.md#affectedcomponent) -[AllowlistSubscription](./Models.md#allowlistsubscription) - [AttackDetail](./Models.md#attackdetail) [AttackerObjective](./Models.md#attackerobjective) [Behavior](./Models.md#behavior) +[BlocklistSubscription](./Models.md#blocklistsubscription) + [CVEEventOutput](./Models.md#cveeventoutput) [CVEExploitationPhase](./Models.md#cveexploitationphase) -[CVEResponseBase](./Models.md#cveresponsebase) +[CVEResponseDetailed](./Models.md#cveresponsedetailed) [CVEsubscription](./Models.md#cvesubscription) @@ -271,8 +295,6 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [FingerprintRuleResponse](./Models.md#fingerprintruleresponse) -[FingerprintRuleSummary](./Models.md#fingerprintrulesummary) - [FingerprintTimelineItem](./Models.md#fingerprinttimelineitem) [GetCVEIPsResponsePage](./Models.md#getcveipsresponsepage) diff --git a/public-openapi.json b/public-openapi.json index 2e5d94e..61e85fe 100644 --- a/public-openapi.json +++ b/public-openapi.json @@ -3,7 +3,7 @@ "info": { "title": "Service API", "description": "This is the API to manage Crowdsec services", - "version": "1.70.29", + "version": "1.70.55", "contact": { "name": "CrowdSec", "url": "https://crowdsec.net", @@ -820,7 +820,7 @@ } }, "409": { - "description": "Blocklist already exists" + "description": "Already exists" }, "422": { "description": "Validation Error", @@ -1097,7 +1097,7 @@ } }, "404": { - "description": "Blocklist not found" + "description": "Not found" }, "422": { "description": "Validation Error", @@ -1155,10 +1155,10 @@ } }, "403": { - "description": "Blocklist is read-only" + "description": "Read-only" }, "404": { - "description": "Blocklist not found" + "description": "Not found" }, "500": { "description": "Internal server error" @@ -1270,7 +1270,7 @@ ], "description": "Blocklist expiration", "examples": [ - "2026-06-11T08:40:13.370581+00:00" + "2026-08-03T13:27:52.522709+00:00" ], "title": "Expiration" }, @@ -2445,6 +2445,33 @@ "description": "Get decisions", "operationId": "getDecisions", "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "description": "Page number", + "default": 1, + "title": "Page" + }, + "description": "Page number" + }, + { + "name": "size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "description": "Page size", + "default": 50, + "title": "Size" + }, + "description": "Page size" + }, { "name": "instance_ids", "in": "query", @@ -2529,6 +2556,70 @@ }, "description": "Filter decisions by IPs (only for IP decisions)" }, + { + "name": "alert_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by associated alert IDs", + "examples": [ + [ + "alert-abc123", + "alert-def456" + ] + ], + "default": [], + "title": "Alert Ids" + }, + "description": "Filter decisions by associated alert IDs" + }, + { + "name": "decision_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by decision IDs", + "examples": [ + [ + "dec-abc123", + "dec-def456" + ] + ], + "default": [], + "title": "Decision Ids" + }, + "description": "Filter decisions by decision IDs" + }, + { + "name": "created_at_from", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "description": "Filter decisions created after this date (inclusive)", + "examples": [ + "2023-01-01T00:00:00Z" + ], + "title": "Created At From" + }, + "description": "Filter decisions created after this date (inclusive)" + }, { "name": "sort_by", "in": "query", @@ -2572,33 +2663,6 @@ "title": "Sort Order" }, "description": "Sort order: 'asc' for ascending, 'desc' for descending" - }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "minimum": 1, - "description": "Page number", - "default": 1, - "title": "Page" - }, - "description": "Page number" - }, - { - "name": "size", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "maximum": 100, - "minimum": 1, - "description": "Page size", - "default": 50, - "title": "Size" - }, - "description": "Page size" } ], "responses": { @@ -2615,9 +2679,6 @@ "404": { "description": "Not found" }, - "500": { - "description": "Internal server error" - }, "422": { "description": "Validation Error", "content": { @@ -2659,7 +2720,7 @@ } }, "409": { - "description": "Conflict: Decision value is in allowlists; cannot create decision." + "description": "Already exists" }, "422": { "description": "Validation Error", @@ -2695,84 +2756,99 @@ ], "responses": { "204": { - "description": "Successful Response" + "description": "No Content" }, "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - } - } - } - } - }, - "/info": { - "get": { - "tags": [ - "Info" - ], - "summary": "Get Me Info", - "description": "Get the current user and organization informations", - "operationId": "getInfo", - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InfoResponse" - } - } - } + "description": "Unprocessable entity" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" } } } }, - "/metrics/remediation": { + "/decisions/aggregated": { "get": { "tags": [ - "Metrics" + "Decisions" ], - "summary": "Get Metrics Remediation", - "description": "Get remediation metrics", - "operationId": "getMetricsRemediation", + "summary": "Get Aggregated Decisions", + "operationId": "getAggregatedDecisions", "parameters": [ { - "name": "start_date", + "name": "page", "in": "query", "required": false, "schema": { - "type": "string", - "format": "date-time", - "description": "Start date of the metrics, default to last day", + "type": "integer", + "minimum": 1, + "description": "Page number", + "default": 1, + "title": "Page" + }, + "description": "Page number" + }, + { + "name": "size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "description": "Page size", + "default": 50, + "title": "Size" + }, + "description": "Page size" + }, + { + "name": "instance_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by instance IDs", "examples": [ - "2025-03-14T09:30:00Z" + [ + "inst-abc123", + "inst-def456" + ] ], - "title": "Start Date" + "default": [], + "title": "Instance Ids" }, - "description": "Start date of the metrics, default to last day" + "description": "Filter decisions by instance IDs" }, { - "name": "end_date", + "name": "tag_ids", "in": "query", "required": false, "schema": { - "type": "string", - "format": "date-time", - "description": "End date of the metrics", + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by tag IDs", "examples": [ - "2025-03-15T09:30:00Z" + [ + "tag-001", + "tag-002" + ] ], - "title": "End Date" + "default": [], + "title": "Tag Ids" }, - "description": "End date of the metrics" + "description": "Filter decisions by tag IDs" }, { - "name": "engine_ids", + "name": "remediation_types", "in": "query", "required": false, "schema": { @@ -2780,20 +2856,20 @@ "items": { "type": "string" }, - "description": "List of engine ids", + "description": "Filter decisions by remediation types", "examples": [ [ - "engine_1", - "engine_2" + "ban", + "captcha" ] ], "default": [], - "title": "Engine Ids" + "title": "Remediation Types" }, - "description": "List of engine ids" + "description": "Filter decisions by remediation types" }, { - "name": "integration_ids", + "name": "ips", "in": "query", "required": false, "schema": { @@ -2801,20 +2877,20 @@ "items": { "type": "string" }, - "description": "List of integration ids", + "description": "Filter decisions by IPs (only for IP decisions)", "examples": [ [ - "integration_1", - "integration_2" + "203.0.113.7", + "198.51.100.42" ] ], "default": [], - "title": "Integration Ids" + "title": "Ips" }, - "description": "List of integration ids" + "description": "Filter decisions by IPs (only for IP decisions)" }, { - "name": "tags", + "name": "alert_ids", "in": "query", "required": false, "schema": { @@ -2822,29 +2898,119 @@ "items": { "type": "string" }, - "description": "List of tags", + "description": "Filter decisions by associated alert IDs", "examples": [ [ - "tag_1", - "tag_2" + "alert-abc123", + "alert-def456" ] ], "default": [], - "title": "Tags" + "title": "Alert Ids" }, - "description": "List of tags" - } - ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetRemediationMetricsResponse" - } - } - } + "description": "Filter decisions by associated alert IDs" + }, + { + "name": "decision_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by decision IDs", + "examples": [ + [ + "dec-abc123", + "dec-def456" + ] + ], + "default": [], + "title": "Decision Ids" + }, + "description": "Filter decisions by decision IDs" + }, + { + "name": "created_at_from", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "description": "Filter decisions created after this date (inclusive)", + "examples": [ + "2023-01-01T00:00:00Z" + ], + "title": "Created At From" + }, + "description": "Filter decisions created after this date (inclusive)" + }, + { + "name": "sort_by", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/AggregatedDecisionsSortBy" + }, + { + "type": "null" + } + ], + "description": "Field to sort by (e.g., created_at, duration)", + "examples": [ + "created_at" + ], + "default": "first_created_at", + "title": "Sort By" + }, + "description": "Field to sort by (e.g., created_at, duration)" + }, + { + "name": "sort_order", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/DecisionsSortOrder" + }, + { + "type": "null" + } + ], + "description": "Sort order: 'asc' for ascending, 'desc' for descending", + "examples": [ + "desc" + ], + "default": "desc", + "title": "Sort Order" + }, + "description": "Sort order: 'asc' for ascending, 'desc' for descending" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AggregatedDecisionsGetResponsePage" + } + } + } + }, + "404": { + "description": "Not found" }, "422": { "description": "Validation Error", @@ -2859,146 +3025,327 @@ } } }, - "/hub/index/{tenant}/{branch}/.index.json": { - "get": { - "description": "Get a (minimized) index file for 'cscli hub update'. May or may not include unused fields\n(content, long descriptions, labels...) or redundant ones (author, name).", - "operationId": "getIndex", + "/decisions/aggregated/{aggregated_id}": { + "delete": { + "tags": [ + "Decisions" + ], + "summary": "Delete Aggregated Decision", + "description": "Delete one decision by ID", + "operationId": "deleteAggregatedDecisions", "parameters": [ { + "name": "aggregated_id", "in": "path", - "name": "branch", "required": true, "schema": { - "title": "Branch", - "type": "string" + "type": "string", + "title": "Aggregated Id" } + } + ], + "responses": { + "204": { + "description": "No Content" }, + "422": { + "description": "Unprocessable entity" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/decisions/aggregated/{aggregated_id}/items/{decision_id}": { + "delete": { + "tags": [ + "Decisions" + ], + "summary": "Delete Aggregated Decision Item", + "description": "Delete one decision by ID", + "operationId": "deleteAggregatedDecisionItem", + "parameters": [ { + "name": "aggregated_id", "in": "path", - "name": "tenant", "required": true, "schema": { - "title": "Tenant", - "type": "string" + "type": "string", + "title": "Aggregated Id" } }, { - "description": "Include content in the index", - "in": "query", - "name": "with_content", - "required": false, + "name": "decision_id", + "in": "path", + "required": true, "schema": { - "default": false, - "description": "Include content in the index", - "title": "With Content", - "type": "boolean" + "type": "string", + "title": "Decision Id" } } ], "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Index" - } - } - }, - "description": "Successful Response" + "204": { + "description": "No Content" }, "422": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - "description": "Validation Error" + "description": "Unprocessable entity" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" } - }, - "summary": "Get a minimized index file (crowdsec only)", + } + } + }, + "/engines": { + "get": { "tags": [ - "Hub" - ] - }, - "head": { - "description": "This endpoint returns cache-related headers for the index file without the full content.\nIt is useful for validating cache, checking resource freshness, and managing client-side\ncache expiration policies. No body content is returned.", - "operationId": "headIndex", + "Engines" + ], + "summary": "Get Engines", + "description": "Get engines for an organization", + "operationId": "getEngines", "parameters": [ { - "in": "path", - "name": "branch", - "required": true, + "name": "tag", + "in": "query", + "required": false, "schema": { - "title": "Branch", - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "null" + } + ], + "description": "List of tags associated with the engines (any of)", + "examples": [ + [ + "web-prod", + "linux" + ] + ], + "title": "Tag" + }, + "description": "List of tags associated with the engines (any of)" }, { - "in": "path", - "name": "tenant", - "required": true, + "name": "page", + "in": "query", + "required": false, "schema": { - "title": "Tenant", - "type": "string" - } + "type": "integer", + "minimum": 1, + "description": "Page number", + "default": 1, + "title": "Page" + }, + "description": "Page number" }, { - "description": "Include content in the index", + "name": "size", "in": "query", - "name": "with_content", "required": false, "schema": { - "default": false, - "description": "Include content in the index", - "title": "With Content", - "type": "boolean" - } + "type": "integer", + "maximum": 100, + "minimum": 1, + "description": "Page size", + "default": 50, + "title": "Size" + }, + "description": "Page size" } ], "responses": { "200": { + "description": "Successful Response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Index" + "$ref": "#/components/schemas/EngineGetResponsePage" } } - }, - "description": "Successful Response" + } }, "422": { + "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } - }, - "description": "Validation Error" + } } - }, - "summary": "Check cache control headers for the index file", - "tags": [ - "Hub" - ] + } } }, - "/hub/index/{tenant}/{branch}/{item_path}": { + "/info": { "get": { - "description": "Get an item's content from its path. This is usually a YAML file.", - "operationId": "getItemContent", - "parameters": [ - { - "in": "path", - "name": "item_path", - "required": true, + "tags": [ + "Info" + ], + "summary": "Get Me Info", + "description": "Get the current user and organization informations", + "operationId": "getInfo", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InfoResponse" + } + } + } + } + } + } + }, + "/metrics/remediation": { + "get": { + "tags": [ + "Metrics" + ], + "summary": "Get Metrics Remediation", + "description": "Get remediation metrics", + "operationId": "getMetricsRemediation", + "parameters": [ + { + "name": "start_date", + "in": "query", + "required": false, "schema": { - "title": "Item Path", - "type": "string" + "type": "string", + "format": "date-time", + "description": "Start date of the metrics, default to last day", + "examples": [ + "2025-03-14T09:30:00Z" + ], + "title": "Start Date" + }, + "description": "Start date of the metrics, default to last day" + }, + { + "name": "end_date", + "in": "query", + "required": false, + "schema": { + "type": "string", + "format": "date-time", + "description": "End date of the metrics", + "examples": [ + "2025-03-15T09:30:00Z" + ], + "title": "End Date" + }, + "description": "End date of the metrics" + }, + { + "name": "engine_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of engine ids", + "examples": [ + [ + "engine_1", + "engine_2" + ] + ], + "default": [], + "title": "Engine Ids" + }, + "description": "List of engine ids" + }, + { + "name": "integration_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of integration ids", + "examples": [ + [ + "integration_1", + "integration_2" + ] + ], + "default": [], + "title": "Integration Ids" + }, + "description": "List of integration ids" + }, + { + "name": "tags", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "List of tags", + "examples": [ + [ + "tag_1", + "tag_2" + ] + ], + "default": [], + "title": "Tags" + }, + "description": "List of tags" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetRemediationMetricsResponse" + } + } } }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/hub/index/{tenant}/{branch}/.index.json": { + "get": { + "description": "Get a (minimized) index file for 'cscli hub update'. May or may not include unused fields\n(content, long descriptions, labels...) or redundant ones (author, name).", + "operationId": "getIndex", + "parameters": [ { "in": "path", "name": "branch", @@ -3016,20 +3363,31 @@ "title": "Tenant", "type": "string" } + }, + { + "description": "Include content in the index", + "in": "query", + "name": "with_content", + "required": false, + "schema": { + "default": false, + "description": "Include content in the index", + "title": "With Content", + "type": "boolean" + } } ], "responses": { "200": { "content": { "application/json": { - "schema": {} + "schema": { + "$ref": "#/components/schemas/Index" + } } }, "description": "Successful Response" }, - "404": { - "description": "No content field" - }, "422": { "content": { "application/json": { @@ -3041,14 +3399,78 @@ "description": "Validation Error" } }, - "summary": "Get an item's content (crowdsec only)", + "summary": "Get a minimized index file (crowdsec only)", "tags": [ "Hub" ] }, "head": { - "description": "This endpoint returns cache-related headers for an item's content. It is useful for validating\ncache, checking resource freshness, and managing client-side cache expiration policies. No body\ncontent is returned.", - "operationId": "headItemContent", + "description": "This endpoint returns cache-related headers for the index file without the full content.\nIt is useful for validating cache, checking resource freshness, and managing client-side\ncache expiration policies. No body content is returned.", + "operationId": "headIndex", + "parameters": [ + { + "in": "path", + "name": "branch", + "required": true, + "schema": { + "title": "Branch", + "type": "string" + } + }, + { + "in": "path", + "name": "tenant", + "required": true, + "schema": { + "title": "Tenant", + "type": "string" + } + }, + { + "description": "Include content in the index", + "in": "query", + "name": "with_content", + "required": false, + "schema": { + "default": false, + "description": "Include content in the index", + "title": "With Content", + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Index" + } + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Check cache control headers for the index file", + "tags": [ + "Hub" + ] + } + }, + "/hub/index/{tenant}/{branch}/{item_path}": { + "get": { + "description": "Get an item's content from its path. This is usually a YAML file.", + "operationId": "getItemContent", "parameters": [ { "in": "path", @@ -3087,6 +3509,9 @@ }, "description": "Successful Response" }, + "404": { + "description": "No content field" + }, "422": { "content": { "application/json": { @@ -3098,22 +3523,79 @@ "description": "Validation Error" } }, - "summary": "Check cache control headers for an item's content", + "summary": "Get an item's content (crowdsec only)", "tags": [ "Hub" ] - } - }, - "/cves": { - "get": { - "tags": [ - "Cves" - ], - "summary": "Get list of CVEs CrowdSec is tracking", - "description": "Get a paginated list of CVEs that CrowdSec is tracking", - "operationId": "getCves", - "parameters": [ - { + }, + "head": { + "description": "This endpoint returns cache-related headers for an item's content. It is useful for validating\ncache, checking resource freshness, and managing client-side cache expiration policies. No body\ncontent is returned.", + "operationId": "headItemContent", + "parameters": [ + { + "in": "path", + "name": "item_path", + "required": true, + "schema": { + "title": "Item Path", + "type": "string" + } + }, + { + "in": "path", + "name": "branch", + "required": true, + "schema": { + "title": "Branch", + "type": "string" + } + }, + { + "in": "path", + "name": "tenant", + "required": true, + "schema": { + "title": "Tenant", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": {} + } + }, + "description": "Successful Response" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + "description": "Validation Error" + } + }, + "summary": "Check cache control headers for an item's content", + "tags": [ + "Hub" + ] + } + }, + "/cves": { + "get": { + "tags": [ + "Cves" + ], + "summary": "Get list of CVEs CrowdSec is tracking", + "description": "Get a paginated list of CVEs that CrowdSec is tracking. Pass detailed=true to also include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE; they are omitted by default to keep the list light.", + "operationId": "getCves", + "parameters": [ + { "name": "query", "in": "query", "required": false, @@ -3199,6 +3681,21 @@ }, "description": "Filter by exploitation phase" }, + { + "name": "detailed", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE in the list. Defaults to false to keep the response lightweight.", + "examples": [ + true + ], + "default": false, + "title": "Detailed" + }, + "description": "Include the heavy detail fields (description, crowdsec_analysis, cwes, references, events, tags) for each CVE in the list. Defaults to false to keep the response lightweight." + }, { "name": "page", "in": "query", @@ -5139,7 +5636,7 @@ "Fingerprints" ], "summary": "Get list of fingerprint rules", - "description": "Get a paginated list of fingerprint rules", + "description": "Get a paginated list of fingerprint rules. Pass detailed=true to also include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each rule; they are omitted by default to keep the list light.", "operationId": "getFingerprintRules", "parameters": [ { @@ -5207,6 +5704,21 @@ }, "description": "Sort order: ascending or descending" }, + { + "name": "detailed", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each fingerprint rule in the list. Defaults to false to keep the response lightweight.", + "examples": [ + true + ], + "default": false, + "title": "Detailed" + }, + "description": "Include the heavy detail fields (description, crowdsec_analysis, references, events, tags) for each fingerprint rule in the list. Defaults to false to keep the response lightweight." + }, { "name": "page", "in": "query", @@ -5888,124 +6400,426 @@ }, "description": "Sort order: ascending or descending" }, - { - "name": "cve_id", - "in": "query", - "required": false, - "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Filter by CVE identifier (exact match)", - "examples": [ - "CVE-2024-12345" - ], - "title": "Cve Id" - }, - "description": "Filter by CVE identifier (exact match)" + { + "name": "cve_id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Filter by CVE identifier (exact match)", + "examples": [ + "CVE-2024-12345" + ], + "title": "Cve Id" + }, + "description": "Filter by CVE identifier (exact match)" + }, + { + "name": "previous_phase", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/CVEExploitationPhase" + }, + { + "type": "null" + } + ], + "description": "Filter by previous exploitation phase name", + "examples": [ + "early_exploitation" + ], + "title": "Previous Phase" + }, + "description": "Filter by previous exploitation phase name" + }, + { + "name": "new_phase", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/CVEExploitationPhase" + }, + { + "type": "null" + } + ], + "description": "Filter by new exploitation phase name", + "examples": [ + "mass_exploitation" + ], + "title": "New Phase" + }, + "description": "Filter by new exploitation phase name" + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "description": "Page number", + "default": 1, + "title": "Page" + }, + "description": "Page number" + }, + { + "name": "size", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "maximum": 100, + "minimum": 1, + "description": "Page size", + "default": 50, + "title": "Size" + }, + "description": "Page size" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ExploitationPhaseChangeEventsResponsePage" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AggregatedDecisionItem": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "ID of the decision aggregated item" + }, + "first_created_at": { + "type": "string", + "format": "date-time", + "title": "First Created At", + "description": "Creation date of the first decision in the group" + }, + "last_created_at": { + "type": "string", + "format": "date-time", + "title": "Last Created At", + "description": "Creation date of the last decision in the group" + }, + "origin": { + "type": "string", + "title": "Origin", + "description": "Origin of the decision" + }, + "scenario": { + "type": "string", + "title": "Scenario", + "description": "Scenario of the decision" + }, + "min_duration": { + "type": "string", + "title": "Min Duration", + "description": "Min duration in the group of decisions" + }, + "max_duration": { + "type": "string", + "title": "Max Duration", + "description": "Max durations in the group of decisions" + }, + "first_expiration": { + "type": "string", + "format": "date-time", + "title": "First Expiration", + "description": "First expiration date in the group of decisions" + }, + "last_expiration": { + "type": "string", + "format": "date-time", + "title": "Last Expiration", + "description": "Last expiration date in the group of decisions" + }, + "count": { + "type": "integer", + "title": "Count", + "description": "Number of decisions in the group" + }, + "machines": { + "additionalProperties": { + "$ref": "#/components/schemas/DecisionMachineState" + }, + "type": "object", + "title": "Machines", + "description": "Machines object (the instance ID is the key) with the decision status and timestamp" + }, + "target": { + "$ref": "#/components/schemas/DecisionTargetModel", + "description": "Target of the decision" + } + }, + "type": "object", + "required": [ + "id", + "first_created_at", + "last_created_at", + "origin", + "scenario", + "min_duration", + "max_duration", + "first_expiration", + "last_expiration", + "count", + "machines", + "target" + ], + "title": "AggregatedDecisionItem" + }, + "AggregatedDecisionsGetResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "ID of the decision aggregated item" + }, + "scope": { + "type": "string", + "title": "Scope", + "description": "Scope of the decision" + }, + "type": { + "type": "string", + "title": "Type", + "description": "Type of the decision" + }, + "value": { + "type": "string", + "title": "Value", + "description": "Value of the decision" + }, + "country": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Country", + "description": "Country associated with the decision" + }, + "as_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "As Name", + "description": "AS name associated with the decision" + }, + "as_num": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "As Num", + "description": "AS number associated with the decision" + }, + "city": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "City", + "description": "City associated with the decision" + }, + "latitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Latitude", + "description": "Latitude associated with the decision" + }, + "longitude": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Longitude", + "description": "Longitude associated with the decision" + }, + "first_created_at": { + "type": "string", + "format": "date-time", + "title": "First Created At", + "description": "Creation date of the first decision in the group" }, - { - "name": "previous_phase", - "in": "query", - "required": false, - "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/CVEExploitationPhase" - }, - { - "type": "null" - } - ], - "description": "Filter by previous exploitation phase name", - "examples": [ - "early_exploitation" - ], - "title": "Previous Phase" - }, - "description": "Filter by previous exploitation phase name" + "last_created_at": { + "type": "string", + "format": "date-time", + "title": "Last Created At", + "description": "Creation date of the last decision in the group" }, - { - "name": "new_phase", - "in": "query", - "required": false, - "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/CVEExploitationPhase" - }, - { - "type": "null" - } - ], - "description": "Filter by new exploitation phase name", - "examples": [ - "mass_exploitation" - ], - "title": "New Phase" - }, - "description": "Filter by new exploitation phase name" + "first_expiration": { + "type": "string", + "format": "date-time", + "title": "First Expiration", + "description": "Expiration date of the first decision in the group" }, - { - "name": "page", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "minimum": 1, - "description": "Page number", - "default": 1, - "title": "Page" - }, - "description": "Page number" + "last_expiration": { + "type": "string", + "format": "date-time", + "title": "Last Expiration", + "description": "Expiration date of the last decision in the group" }, - { - "name": "size", - "in": "query", - "required": false, - "schema": { - "type": "integer", - "maximum": 100, - "minimum": 1, - "description": "Page size", - "default": 50, - "title": "Size" + "decisions": { + "items": { + "$ref": "#/components/schemas/AggregatedDecisionItem" }, - "description": "Page size" + "type": "array", + "title": "Decisions", + "description": "List of decisions in the group" } + }, + "type": "object", + "required": [ + "id", + "scope", + "type", + "value", + "decisions" ], - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ExploitationPhaseChangeEventsResponsePage" - } + "title": "AggregatedDecisionsGetResponse" + }, + "AggregatedDecisionsGetResponsePage": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/AggregatedDecisionsGetResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" } - } + ], + "title": "Total" }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } + "page": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" } - } + ], + "title": "Page" + }, + "size": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Size" + }, + "pages": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Pages" + }, + "links": { + "$ref": "#/components/schemas/Links", + "readOnly": true } - } - } - } - }, - "components": { - "schemas": { + }, + "type": "object", + "required": [ + "items", + "page", + "size", + "links" + ], + "title": "AggregatedDecisionsGetResponsePage" + }, + "AggregatedDecisionsSortBy": { + "type": "string", + "enum": [ + "first_created_at", + "first_expiration" + ], + "title": "AggregatedDecisionsSortBy" + }, "AllowlistCreateRequest": { "properties": { "name": { @@ -6739,6 +7553,19 @@ ], "title": "AllowlistSubscribersCount" }, + "AllowlistSubscription": { + "properties": { + "id": { + "type": "string", + "title": "Id" + } + }, + "type": "object", + "required": [ + "id" + ], + "title": "AllowlistSubscription" + }, "AllowlistSubscriptionRequest": { "properties": { "ids": { @@ -7210,6 +8037,12 @@ "title": "Tags", "description": "Classification tags", "default": [] + }, + "store_full_content_in_s3": { + "type": "boolean", + "title": "Store Full Content In S3", + "description": "Whether to store the full blocklist content in S3 or not", + "default": false } }, "additionalProperties": false, @@ -7592,61 +8425,27 @@ "page", "size", "links" - ], - "title": "BlocklistSubscriberEntityPage" - }, - "BlocklistSubscribersCount": { - "properties": { - "entity_type": { - "$ref": "#/components/schemas/SubscriberEntityType", - "description": "Subscriber entity type" - }, - "count": { - "type": "integer", - "title": "Count", - "description": "Subscriber entity count" - } - }, - "type": "object", - "required": [ - "entity_type", - "count" - ], - "title": "BlocklistSubscribersCount" - }, - "BlocklistSubscription": { - "properties": { - "id": { - "type": "string", - "title": "Id" - }, - "remediation": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Remediation" - }, - "name": { - "type": "string", - "title": "Name" + ], + "title": "BlocklistSubscriberEntityPage" + }, + "BlocklistSubscribersCount": { + "properties": { + "entity_type": { + "$ref": "#/components/schemas/SubscriberEntityType", + "description": "Subscriber entity type" }, - "label": { - "type": "string", - "title": "Label" + "count": { + "type": "integer", + "title": "Count", + "description": "Subscriber entity count" } }, "type": "object", "required": [ - "id", - "name", - "label" + "entity_type", + "count" ], - "title": "BlocklistSubscription" + "title": "BlocklistSubscribersCount" }, "BlocklistSubscriptionRequest": { "properties": { @@ -7656,7 +8455,7 @@ }, "type": "array", "title": "Ids", - "description": "List of subscriber entity id" + "description": "List of subscriber entity id. Must be empty for organization type subscription" }, "entity_type": { "$ref": "#/components/schemas/SubscriberEntityType" @@ -7671,7 +8470,7 @@ } ], "title": "Remediation", - "description": "Remediation" + "description": "Remediation, optional for firewall integration and required for other entity types" } }, "additionalProperties": false, @@ -8247,6 +9046,35 @@ ], "title": "DecisionCreateResponse" }, + "DecisionMachineState": { + "properties": { + "timestamp": { + "type": "string", + "format": "date-time", + "title": "Timestamp", + "description": "Date of when the state has been set" + }, + "state": { + "$ref": "#/components/schemas/DecisionMachineStateEnum", + "description": "State of the decision" + } + }, + "type": "object", + "required": [ + "timestamp", + "state" + ], + "title": "DecisionMachineState" + }, + "DecisionMachineStateEnum": { + "type": "string", + "enum": [ + "PENDING_CREATE", + "PENDING_DELETE", + "APPLIED" + ], + "title": "DecisionMachineStateEnum" + }, "DecisionResponse": { "properties": { "created_at": { @@ -8500,6 +9328,184 @@ ], "title": "DecisionsSortOrder" }, + "EngineGetResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "ID of the engine" + }, + "organization_id": { + "type": "string", + "title": "Organization Id", + "description": "ID of the owner organization" + }, + "created_at": { + "type": "string", + "format": "date-time", + "title": "Created At", + "description": "Time the engine was created" + }, + "entity_type": { + "$ref": "#/components/schemas/EntityType", + "description": "Type of the entity", + "default": "engine" + }, + "blocklists": { + "items": { + "$ref": "#/components/schemas/capi__entities__lists_subscriber_entity__BlocklistSubscription" + }, + "type": "array", + "title": "Blocklists", + "default": [] + }, + "allowlists": { + "items": { + "$ref": "#/components/schemas/AllowlistSubscription" + }, + "type": "array", + "title": "Allowlists", + "default": [] + }, + "version": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Version" + }, + "enrolled_at": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Enrolled At" + }, + "os": { + "$ref": "#/components/schemas/OperatingSystem" + }, + "meta": { + "additionalProperties": true, + "type": "object", + "title": "Meta", + "default": {} + }, + "last_pull": { + "type": "string", + "format": "date-time", + "title": "Last Pull", + "description": "Last time the engine pulled CAPI" + }, + "last_push": { + "type": "string", + "format": "date-time", + "title": "Last Push", + "description": "Last time the engine pushed to CAPI" + }, + "last_login": { + "type": "string", + "format": "date-time", + "title": "Last Login", + "description": "Last time the engine logged in CAPI" + }, + "tags": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Tags", + "description": "List of tags associated with the engine", + "default": [] + } + }, + "type": "object", + "required": [ + "id", + "organization_id", + "created_at" + ], + "title": "EngineGetResponse" + }, + "EngineGetResponsePage": { + "properties": { + "items": { + "items": { + "$ref": "#/components/schemas/EngineGetResponse" + }, + "type": "array", + "title": "Items" + }, + "total": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Total" + }, + "page": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Page" + }, + "size": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Size" + }, + "pages": { + "anyOf": [ + { + "type": "integer", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Pages" + }, + "links": { + "$ref": "#/components/schemas/Links", + "readOnly": true + } + }, + "type": "object", + "required": [ + "items", + "page", + "size", + "links" + ], + "title": "EngineGetResponsePage" + }, "EntityType": { "type": "string", "enum": [ @@ -8695,7 +9701,7 @@ }, "blocklists": { "items": { - "$ref": "#/components/schemas/BlocklistSubscription" + "$ref": "#/components/schemas/src__api__v1__models__integrations__BlocklistSubscription" }, "type": "array", "title": "Blocklists", @@ -8855,7 +9861,7 @@ }, "blocklists": { "items": { - "$ref": "#/components/schemas/BlocklistSubscription" + "$ref": "#/components/schemas/src__api__v1__models__integrations__BlocklistSubscription" }, "type": "array", "title": "Blocklists", @@ -9127,7 +10133,7 @@ }, "blocklists": { "items": { - "$ref": "#/components/schemas/BlocklistSubscription" + "$ref": "#/components/schemas/src__api__v1__models__integrations__BlocklistSubscription" }, "type": "array", "title": "Blocklists", @@ -9320,6 +10326,35 @@ ], "title": "MetricUnits" }, + "OperatingSystem": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "version": { + "type": "string", + "title": "Version" + }, + "family": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Family" + } + }, + "type": "object", + "required": [ + "name", + "version" + ], + "title": "OperatingSystem" + }, "OriginMetrics": { "properties": { "origin": { @@ -9848,21 +10883,79 @@ "msg", "type" ], - "title": "ValidationError" + "title": "ValidationError" + }, + "VendorSubscription": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "Vendor ID" + } + }, + "type": "object", + "required": [ + "id" + ], + "title": "VendorSubscription" + }, + "capi__entities__lists_subscriber_entity__BlocklistSubscription": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "remediation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remediation" + } + }, + "type": "object", + "required": [ + "id" + ], + "title": "BlocklistSubscription" }, - "VendorSubscription": { + "src__api__v1__models__integrations__BlocklistSubscription": { "properties": { "id": { "type": "string", - "title": "Id", - "description": "Vendor ID" + "title": "Id" + }, + "remediation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remediation" + }, + "name": { + "type": "string", + "title": "Name" + }, + "label": { + "type": "string", + "title": "Label" } }, "type": "object", "required": [ - "id" + "id", + "name", + "label" ], - "title": "VendorSubscription" + "title": "BlocklistSubscription" }, "AppsecConfigIndex": { "properties": { @@ -11148,19 +12241,6 @@ "title": "AffectedComponent", "description": "Affected Component in a CVE" }, - "AllowlistSubscription": { - "properties": { - "id": { - "type": "string", - "title": "Id" - } - }, - "type": "object", - "required": [ - "id" - ], - "title": "AllowlistSubscription" - }, "AttackDetail": { "properties": { "name": { @@ -11230,6 +12310,30 @@ ], "title": "Behavior" }, + "BlocklistSubscription": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "remediation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Remediation" + } + }, + "type": "object", + "required": [ + "id" + ], + "title": "BlocklistSubscription" + }, "CVEEventOutput": { "properties": { "name": { @@ -11268,17 +12372,20 @@ "enum": [ "insufficient_data", "early_exploitation", + "rapid_escalation", + "active_exploitation", + "background_noise", + "limited_exploitation", "fresh_and_popular", "targeted_exploitation", "mass_exploitation", - "background_noise", "unpopular", "wearing_out", "unclassified" ], "title": "CVEExploitationPhase" }, - "CVEResponseBase": { + "CVEResponseDetailed": { "properties": { "id": { "type": "string", @@ -11421,6 +12528,62 @@ } ], "description": "Threat context (attacker/defender countries, industries, objectives)" + }, + "tags": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Tags", + "description": "Tags associated with the CVE" + }, + "references": { + "items": { + "type": "string" + }, + "type": "array", + "title": "References", + "description": "List of references for the CVE" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description", + "description": "Description of the CVE" + }, + "crowdsec_analysis": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Crowdsec Analysis", + "description": "CrowdSec analysis of the CVE" + }, + "cwes": { + "items": { + "$ref": "#/components/schemas/CWE" + }, + "type": "array", + "title": "Cwes", + "description": "List of CWEs associated with the CVE" + }, + "events": { + "items": { + "$ref": "#/components/schemas/CVEEventOutput" + }, + "type": "array", + "title": "Events", + "description": "List of events related to the CVE" } }, "type": "object", @@ -11435,8 +12598,8 @@ "has_public_exploit", "exploitation_phase" ], - "title": "CVEResponseBase", - "description": "GET CVE ID Response" + "title": "CVEResponseDetailed", + "description": "List item able to carry the heavy detail fields.\n\nThe list endpoint only populates these when ``detailed=true`` is passed;\notherwise they are left unset and stripped from the response via\n``response_model_exclude_unset``. All fields are therefore optional so a\nlean list item stays valid. Values are served straight from the cache\n(same source as the detail endpoint)." }, "CVEsubscription": { "properties": { @@ -11876,138 +13039,6 @@ ], "title": "FingerprintRuleResponse" }, - "FingerprintRuleSummary": { - "properties": { - "id": { - "type": "string", - "title": "Id", - "description": "Fingerprint rule identifier" - }, - "name": { - "type": "string", - "title": "Name", - "description": "Fingerprint rule name" - }, - "title": { - "type": "string", - "title": "Title", - "description": "Fingerprint rule title" - }, - "affected_components": { - "items": { - "$ref": "#/components/schemas/AffectedComponent" - }, - "type": "array", - "title": "Affected Components", - "description": "List of affected components" - }, - "crowdsec_score": { - "type": "integer", - "maximum": 10.0, - "minimum": 0.0, - "title": "Crowdsec Score", - "description": "Live Exploit Tracker score for the fingerprint rule" - }, - "opportunity_score": { - "type": "integer", - "maximum": 5.0, - "minimum": 0.0, - "title": "Opportunity Score", - "description": "Opportunity score", - "default": 0 - }, - "momentum_score": { - "type": "integer", - "maximum": 5.0, - "minimum": 0.0, - "title": "Momentum Score", - "description": "Momentum score", - "default": 0 - }, - "first_seen": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ], - "title": "First Seen", - "description": "First seen date" - }, - "last_seen": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ], - "title": "Last Seen", - "description": "Last seen date" - }, - "nb_ips": { - "type": "integer", - "minimum": 0.0, - "title": "Nb Ips", - "description": "Number of unique IPs observed" - }, - "rule_release_date": { - "anyOf": [ - { - "type": "string", - "format": "date-time" - }, - { - "type": "null" - } - ], - "title": "Rule Release Date", - "description": "Release date of the fingerprint rule" - }, - "exploitation_phase": { - "$ref": "#/components/schemas/ExploitationPhase", - "description": "Current exploitation phase" - }, - "adjustment_score": { - "anyOf": [ - { - "$ref": "#/components/schemas/AdjustmentScore" - }, - { - "type": "null" - } - ], - "description": "Score adjustment details" - }, - "threat_context": { - "anyOf": [ - { - "$ref": "#/components/schemas/ThreatContext" - }, - { - "type": "null" - } - ], - "description": "Threat context (attacker/defender countries, industries, objectives)" - } - }, - "type": "object", - "required": [ - "id", - "name", - "title", - "affected_components", - "crowdsec_score", - "nb_ips", - "exploitation_phase" - ], - "title": "FingerprintRuleSummary" - }, "FingerprintTimelineItem": { "properties": { "timestamp": { @@ -12350,7 +13381,7 @@ "properties": { "items": { "items": { - "$ref": "#/components/schemas/CVEResponseBase" + "$ref": "#/components/schemas/CVEResponseDetailed" }, "type": "array", "title": "Items" @@ -12459,7 +13490,7 @@ "properties": { "items": { "items": { - "$ref": "#/components/schemas/FingerprintRuleSummary" + "$ref": "#/components/schemas/FingerprintRuleResponse" }, "type": "array", "title": "Items" diff --git a/pyproject.toml b/pyproject.toml index 3475644..9784d4d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "crowdsec_service_api" -version = "1.128.0" +version = "v0.18.5" license = { text = "MIT" } authors = [ { name="crowdsec", email="info@crowdsec.net" } diff --git a/uv.lock b/uv.lock index 2dd3cda..a9fa1b9 100644 --- a/uv.lock +++ b/uv.lock @@ -4,52 +4,52 @@ requires-python = ">=3.11" [[package]] name = "annotated-types" -version = "0.7.0" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/56/a8120250d128bed162cd73c76d45f6ef9991f3e068f62a8ee060afa3104a/annotated_types-0.8.0.tar.gz", hash = "sha256:13b2beaad985e05e2d6407ee4c4f35590b11f8d693a258a561055cac8f64cab7", size = 15893, upload-time = "2026-07-23T20:16:13.995Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, + { url = "https://files.pythonhosted.org/packages/99/91/8acff4f5e50511b911bbccb72b8628a49c68ce14148cd9f6431094859a90/annotated_types-0.8.0-py3-none-any.whl", hash = "sha256:f072f4d804ea359e4eaf198b1af7a8b0943881a87f31bb764f8bf219bb9419e0", size = 13427, upload-time = "2026-07-23T20:16:12.938Z" }, ] [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] [[package]] name = "botocore" -version = "1.43.27" +version = "1.43.67" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jmespath" }, { name = "python-dateutil" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/4e/db50ef135f1d9ffc85e209a124004a5829d8f12f4a7a0afdf380cb19866d/botocore-1.43.27.tar.gz", hash = "sha256:2093c316c24214e50e18640b1869513b759bb8cc48b95b004a8306cb9f0d6703", size = 15504242, upload-time = "2026-06-10T19:38:25.389Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/1c/3a75deae60e36bd0ee5c27d040384756b2ea1c90bd7c8c9658335a18b4f5/botocore-1.43.67.tar.gz", hash = "sha256:6fe5cfa0c8676ba809efe505b618ec00f30d1af2d014bf316a7aa4ee86accb20", size = 15889514, upload-time = "2026-08-07T19:30:15.638Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/46/05b227b34e434b54867c2c942b0bfbbe2fe41789c18bb15ef787d03e9a56/botocore-1.43.27-py3-none-any.whl", hash = "sha256:4976544e652d5a1d8eca135da019f8e1c2d749efa2f9a31a8fb8c76f1895a40b", size = 15190293, upload-time = "2026-06-10T19:38:22.298Z" }, + { url = "https://files.pythonhosted.org/packages/21/be/38af8e96f3d200c9d34eab8e9f69a4468388ed6030ea04ff012974e5af5a/botocore-1.43.67-py3-none-any.whl", hash = "sha256:48ab8e9fac26fbc2a700d57010251003e6a5f731cf74d8540fb796bc8f3fc0ef", size = 15575924, upload-time = "2026-08-07T19:30:12.116Z" }, ] [[package]] name = "certifi" -version = "2026.5.20" +version = "2026.7.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, ] [[package]] name = "crowdsec-service-api" -version = "1.128.0" +version = "0.18.5" source = { editable = "." } dependencies = [ { name = "botocore" }, @@ -299,11 +299,11 @@ wheels = [ [[package]] name = "typing-extensions" -version = "4.15.0" +version = "4.16.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, ] [[package]] @@ -320,11 +320,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2026.2" +version = "2026.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, ] [[package]]