From 825b51c0bb5f996891588df854a7a631da9325d6 Mon Sep 17 00:00:00 2001 From: he2ss <19668340+he2ss@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:02:00 +0000 Subject: [PATCH] Update python SDK v0.17.0 --- crowdsec_service_api/__init__.py | 9 +- .../__pycache__/__init__.cpython-311.pyc | Bin 5024 -> 5163 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 -> 166715 bytes crowdsec_service_api/models.py | 415 +++++-- .../__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__/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/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/Fingerprints.md | 6 +- doc/Models.md | 105 +- doc/README.md | 16 +- public-openapi.json | 1069 ++++++++++++++--- pyproject.toml | 2 +- uv.lock | 26 +- 42 files changed, 1579 insertions(+), 342 deletions(-) diff --git a/crowdsec_service_api/__init__.py b/crowdsec_service_api/__init__.py index f2c57ce..d57f24c 100644 --- a/crowdsec_service_api/__init__.py +++ b/crowdsec_service_api/__init__.py @@ -33,6 +33,10 @@ class Server(Enum): 'TrackerTags', 'Fingerprints', 'TrackerEvents', + 'AggregatedDecisionItem', + 'AggregatedDecisionsGetResponse', + 'AggregatedDecisionsGetResponsePage', + 'AggregatedDecisionsSortBy', 'AllowlistCreateRequest', 'AllowlistCreateResponse', 'AllowlistGetItemsResponse', @@ -84,6 +88,8 @@ class Server(Enum): 'CtiScenario', 'DecisionCreateRequest', 'DecisionCreateResponse', + 'DecisionMachineState', + 'DecisionMachineStateEnum', 'DecisionResponse', 'DecisionTargetModel', 'DecisionTargetType', @@ -138,7 +144,7 @@ class Server(Enum): 'Behavior', 'CVEEventOutput', 'CVEExploitationPhase', - 'CVEResponseBase', + 'CVEResponseDetailed', 'CVEsubscription', 'CWE', 'Classification', @@ -149,7 +155,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..a08028fa6a2cea703cf91534ab57130476abb526 100644 GIT binary patch delta 284 zcmZ3WzFLECIWI340}z~D>7SJ=x{77N<^5;khO*=9Zb4 zo?28;l$lo&RGO0-T$-DkSX8;$k~f!?@#5y?LYa)xMNHgG9~cm113L&7$p9S&05`@q ABme*a diff --git a/crowdsec_service_api/__pycache__/base_model.cpython-311.pyc b/crowdsec_service_api/__pycache__/base_model.cpython-311.pyc index 98b9f58568825bc4f734f1e579444f5920d5390c..a22db72ca775baab41e37d9e20ed839854a00323 100644 GIT binary patch delta 56 zcmbQGG)akjIWI340}yOl>A#U%nOQDaKO;XkRlle-FE6!7KfXM_C|kd%ELAtLAQMP$ J4q-mV3joq}5)A+V 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..a8d0d67d59756c427d1ffafee0592a994790c5ef 100644 GIT binary patch delta 56 zcmca%bHRptIWI340}yOl>A#Wt9HU%_enx(7s(w*vUS4XEetda;QMP_jS*mVgK_-yi J{GTyW1^^Q_6S4pR 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..78986796128e5efe38a259fb5e74f38a9a1abcda 100644 GIT binary patch delta 38106 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 JHD3&J{tqNUd7&@Nhd9mq+EF_FrkB;xgQ2y61A2 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

B0m`fNvaiVY2Z) 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 UIe4cMq*z9-P%bO3k+SXn2ly05?*IS* diff --git a/crowdsec_service_api/models.py b/crowdsec_service_api/models.py index d2625ae..8312f83 100644 --- a/crowdsec_service_api/models.py +++ b/crowdsec_service_api/models.py @@ -1,6 +1,6 @@ # generated by datamodel-codegen: # filename: -# timestamp: 2026-06-11T08:42:40+00:00 +# timestamp: 2026-07-09T14:01:49+00:00 from __future__ import annotations @@ -13,6 +13,27 @@ 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', @@ -244,6 +249,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): @@ -420,6 +432,12 @@ class DecisionCreateResponse(BaseModelSdk): ] +class DecisionMachineStateEnum(StrEnum): + PENDING_CREATE = 'PENDING_CREATE' + PENDING_DELETE = 'PENDING_DELETE' + APPLIED = 'APPLIED' + + class DecisionTargetType(StrEnum): ORG = 'org' TAG = 'tag' @@ -1380,7 +1398,7 @@ class BlocklistsUploadBlocklistContentQueryParameters(BaseModelSdk): Optional[datetime], Field( description='Blocklist expiration', - examples=['2026-06-11T08:40:13.370581+00:00'], + examples=['2026-07-08T08:42:28.511313+00:00'], title='Expiration', ), ] = None @@ -1587,6 +1605,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 +1643,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,15 +1683,99 @@ 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')] @@ -1749,6 +1881,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 +2317,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 @@ -2647,6 +2795,16 @@ class BlocklistSubscriptionRequest(BaseModelSdk): ] = 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') @@ -3728,7 +3886,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 +3966,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 +4073,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 +4188,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 +4197,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 +4524,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..08a7f5031bedf74818724b78e3c11d3dd9f93a06 100644 GIT binary patch delta 38 scmX@lc$|@YIWI340}yOl=|7R%lrd_eqcB%dS*mVgL8g9D*~GL=0LfenUjP6A 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..239698571feeb216e57cdba6a3bcdcafe74bcd61 100644 GIT binary patch delta 66 zcmeyG@-~HgIWI340}z~D>A#UXmYKE8K+n*8axQa-T#SB3er~FMQE6UYYLR|?d45s0 TeoBfIWI340|=CAZRCz+W;N6^G_shS%N(MXqMwnUo2p+_nwOVaq#s|NUzDw% WoS#>mpOdPaSda;2Z@$mmrw0H-4H%^W diff --git a/crowdsec_service_api/services/__pycache__/blocklists.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/blocklists.cpython-311.pyc index d87cdb1bac7f4880ebeb44a85f3d6e4d5ffdd4cb..2351f2ad84641acbe6e76ed2b78571495375bc7a 100644 GIT binary patch delta 66 zcmbPO)>+2AoR^o20SL~n^xw#RmzlN9K+n*8@>}K*xfuP7{M=OiqSCy))FS=(^8BJ~ T{i3o|-Nb@SAiX)AWtABK&_Wiv delta 74 zcmeAyn^?xZoR^o20R&35Hgex(W;N6^G_sidmN`T%ML#1yH&wr=G%qi;NI$+jzbIQj WIX|yBKPOc;u^AE&U5E6k6z?jdpAx0&YvTMld?b2GNZ;Hst#{i=}No3JwPn z|e_lVTQYVvvThlUi zotH;#|M5EDZs2y{i|8PO!2O2Pnta+mY z5Uv%%1vSqQYk{Hytaa0U0}Vs8&_(Gi84V9hvAB4u<|qB^oa24m|kIA#btMB)iC@YRn&wAFjI)y2bbA zCUGxh*706($K2R_5Uu?P&me5o;`_zdid*zq+$3H)YPq{Z=o3nOJ@k?iB!^-1AURO_ zTe-2r#$T=NxKZW3kV9E6va>~wyWJvei;`odBenru%SKh5n5kBpc}r>6?J?3LZrh`V zr_rNVsE*czR^W@sBM8{!?dm=07$E7=XO1P^!)@HEHRMWw)?2QQb**KWKfIx}O-I*U zN6Fiz!|sbuQqo;-x1k48E+@pAHz)>twspN%eBCik?}^I#&NoIrEcqJxbo)mk+z8jY zc?y;>J@3Gt;A~Z=Oq&61TC^S;xAgduzY`x-KfEvb>0->n8*m>6;#xM3E4*whjL&8B z;}m8}A2?3FDUAix|MhsQ)`jM7)aoi5D9f@UivP84Xam#ifb!Xzel z$RrX@kv~e=@OzuGNJXYF7AY$unJJdd3US6 zw<8glLSwYOdn3QiO`63RJ2Yu78YTC|r_p6k zv0{%_j2bsduE5Pe$BHviF>3~k6llBI*1O{GtJG=+MXL8yRBdUMY>tXeTh`&Ss^XB$ z{z^SsomIsx*i|y`p45>pQB0hx)1Cl+=sEsLfDwC;@Dd@s$s5lP%H$Aa{P@eop0(=T LzqWDW0f_rAWMtHq delta 2176 zcma)6OKe+J5WUxV{`=Xn<4-F3T2zpT&VQ(VKFL_BaG3@6{h&b>)QczRCHD^&fL7)R-cWxRhF*mf-1u-6xv0p?rGkE7rYB!wv z)E?HUx|W#)HTj{0E)f_Iycc*kNJNFt#(LBsl;mfDX4g#%`t-BjCI^8K2n5C5J{A(^ zeJ$q(bqD0%afzIxGy3pxqQv+_+xPlpJh^)xH%ce^XA|><1(whj^LeHvPQ6{w-b`c) z`BEXrhSJ6Cy3Nl^*SJqVE=$Gq8>}XkvhT2rk-ukh^Ci8R8u@LxM{mlKB{RR?8`c}L zq+S@bw zkI6r88`zNlnk_^RLWXHNSvAVH9&`JSz2DnX9aK}3%IsWO!xUaV?1<5$)sv1XU0<`w zr?6(xsd+=ED^7)u^1IF$U-esf$kiJceLj!j{RmNnZQ9PcrYNg^>-tdl=twsT&3fV> zblEc`gjPJAk*3f#>CeV|+~*ym?^cg_FFi>^e|L_6PO=LgA~aO!N-9F%JS$;kmrycx@=$_3=yBm!{ zY2H{rmNuxE8)FxxE#mcUC-OA?hu@6--v1m%h7kGy6sn;^(Y3l>P$GOqDIRQt*O0a! zLnjfQK{$l)EW#AR5rm@%#}Hmdcm?4k!t1;;5HV!XoaZ+NcJ(fxSq7-tj%V{3_F}P| zg;$w&4*eJSy@6r+5)bcsYuWAj+3l@6eEJlv6U1K}zN>8?EYLw3a?OUXw`{9xOP_u z2hZr}FuxMNposS+zP>dl0&b2LK)@&Wz%;Q?P#1ITfc6m|Puw~(CDDUW`PTm~?fs*1 K`qwr=Lf~I+LA^l$ diff --git a/crowdsec_service_api/services/__pycache__/decisions.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/decisions.cpython-311.pyc index d705642e1226909db262b964ed97acbc6e614089..d571cd55a96fcc9249bbdd34e8269ba53f2916d0 100644 GIT binary patch literal 7214 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 delta 1172 zcmZ`%OK1~87@kRYvyWtxX0zG68hq5(T5DP#wZ5uV@qr!`MKB;CW~LfVvvHE*tLjBm zP^2;i1wD9D@QLWztAZC%4`K1(#e)}FA1A#yGpV3eXPJ-h`{ti-X8+xXgI{Jv?ihv+ z_!wJw*w!CMHo|ONXaK%`W8?z31~4c<1PTF^qE(nWdl0mr0Kh?{+=c+$W3xS&ry%uD z4{1mrQBer1SW5pY1SmXB8`E@TnvP-(2ig8zQxT|>8Om>gF{~q#O$moaOcXyS7sANG z2Gfxd2_?=+g($MIG!j5b#yc;9jvpr5n=&N7?M(ELKFO4hD15T8_Q+oToFs`AxTd6gRW2EYJ zRNXS+S0nu#`WSdB!Hqh5IoyacNq4AQB^#vX&0GN+tX+~L25u*ElMS9LM(ySir7MPpSwME28s_Sx`e2rVD{ P8z+>{V&&U^)xVt&Hd!Qq`GtiET(j&$`bu zv$Heb&dlxQ!`~d%u4$So!E^Ka&J*KbYGb6qjVe5kZV^d(Q@SEiX_6Syq{(RINix1* zIHs+JlPXjWl^g*L4RmXT?gHIbp}VQwsG+Lifmf!EX_w)>V3~9pwNwUOo|X;Y1$k1l zOZYR|ggx+7j?(@G4B(npjUWVYgL8CQXPMRj%{uV$h>-2Vt4zT=tr#I#tu^YY57&S~ z15oe_zeb^lK@UKN4q|YK0SBZo&jR2_@AkqAmG)pE|?~!!9z;*LY%c;KJ{vrO@ytnC4?0EVljj@?j zin7?$=`?#S#!k_mcqRe=d`AA9jPuo&_O*&ZbMZuy8U;BMKSm33HgT37wJo(!l2A6o z`$&bxUy%$lZ{xR>28)uA=IwlQdyB=IkmeNry<>w#1)GCs8d_quImetjXa7w8SeB$u z;VnJ$&fJqh0)t^SEOSzV%sKhP_AY+L`Ww;sgso|awZm$`NmE89ok-=TW>~UdPNt8t z7}!}aLJxWBPl^8tMrpTuJ0oVJL44zF6Q3}ILDK~oj zB^EqNy0>MrV`3DtdvftyR<~EwLy%jcD3=<8e9^O;OyoCvFPoyzEes~y z3`F{Pm%oOr;~V{Bjxv=#{))eqf8qD(qB8%hD2x76+UpQD`vBm)RCMwmR3D%6+VXD) z>P?$o1?yfWcF$58DfSXvAOZe!$1wSmoAs}vW5^|f&k8g4;|$kyYAO$b{Mwjpds*nzMUVK2fyPCL7;2T(lBuXO6x zX%tWLA3NJ>@M~c?gwqJK{Bh?X8RfCA>_VvNzR!Q(7bv=emR+PMA^z?TUNyfvU$h95 zRr1zf?YZaaDB6UsNdEBcaL+woZ_zFcs#F^+I)vzy+}@%lL>G#spf!@cqv#P<5xCALUb3`dJkoJ8aea)xH2`-rx&=GvJr9%C@k@r`vXF*d#xsI)(xOCB$sWJzP8 zSY$@>@adBAQu1i2XqF0WG+WNE$Y4sJLe#oZmCD&;tR|WHv+Sr0H|>5!;a$E&bh;?R zp00kYEl(Eh@N(BiYJ=~c(^d!96zSvf$RaMP85 z*X6s^1zFnwT(J4W3GQKxB8+gT*$rlvIW=awxC&p|4pL9mZNE)JL+CS%km8`(jWLwR z5jMj=jxEy@C~iTR`djV-RMKh48k^qDTMZ<#x$H%o~J|AuayIqXfsa|T}3iS zTe5I00vRz&!zJhyvy*-W!aHeWFJRk9URbF`E|@L7Y!`=K7V@7+sZsLuJ8 zE%7JT0_i-(>7-yPfK;9hq@7JFDL51ugc||BCW`UD>To}BN-guS<}V!1le!CTDSjyW zY}I$W`z$l_+`8Woybf?tvl^TDOUzDVibQM0REXl)+B#S7olUQfNlhx3%!;No%7f9? z9+R4_oPDKG%IbVx7J_B^X7$%#zvaQXY)=>tJu}lxDC?}iDy*#|FJj&`J93(SSnUZP zu*i*WKx}Y_bd?$@xV~=T&8qX+5r>QeNB7AEO2o$u0U&AYp(=Z|J9C1Y2mQl8INjvG0?Vp=#vB1Q;f=Q*66 zhS|;m2*n+6bI?zJgC7SUNk1Ih)_T9T#}qgpdmQ}@82u!|8io8Wc8LB^jmP5;U9h&o z@hG(sQL>9eUD|5z8sC&2@pU!IM!s0tyHH?zS3Xw8EPORINB;ny_CP{iLP;5l%B#xoFTUbTVM(7nx`K;l4f!?~6zpLqyJr+6+ t%ZXP#V))!h(2J0QXtI~S4O7V*vt|dKs*^|KJClEkmA@aeZiIQXe*x3I$L9b5 diff --git a/crowdsec_service_api/services/__pycache__/hub.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/hub.cpython-311.pyc index 9c5bafe5e48a74bcc5b7523b7ea994a416066e27..9fc78c4cd7c6d1b4a891422a5eaf4dfb62038af0 100644 GIT binary patch delta 66 zcmX>rc~p{nIWI340}z~D>A#V?f{C@vK+n*8atBk0T&R9Ver~FMQE6UYYLR|?d45s0 Teoqc~+8pIWI340|=CAZRDmpOdPaSda;2Z~nz}niBvF78o7? diff --git a/crowdsec_service_api/services/__pycache__/info.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/info.cpython-311.pyc index 0cca044724879ad51a383a04a974b9abcb753622..be9dc3e639624f924ff5f5251a6ffe7f3f9c2910 100644 GIT binary patch delta 66 zcmey&|C*nBIWI340}z~D>A#UXl!>*>K+n*8aw=1ZT$p}Fer~FMQE6UYYLR|?d45s0 Teo delta 74 zcmaFO|CyhAIWI340|=CAZR8GRVl~t=G_shS$`qm&r=O9Zo2p+_nwOVaq#s|NUzDw% WoS#>mpOdPaSda;2Z@$hX!U_NkIu|nl diff --git a/crowdsec_service_api/services/__pycache__/integrations.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/integrations.cpython-311.pyc index 53c1b9a9ec792af93fa36c27f740d87492cd99cb..6c6b0a236119f275473878607ed194f407406e8f 100644 GIT binary patch delta 66 zcmeCSZ?@-N&dbZi00d`O`fub`XJ#!k&@(ijY|b1a7pI?*pPQ;*RGOEUTBILeo?n!$ TUsRT=n^=$uq&IJ5z9kI+b&M4> delta 74 zcmZp+@3!Y&&dbZi00O028@bh)Sq=3JjVvacGl!_9>1X8Urs@}!=H;ap>BpDn7iH@w W=jRpY=cMW;7Gy%%n^!X5k_G_W0~a6w diff --git a/crowdsec_service_api/services/__pycache__/metrics.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/metrics.cpython-311.pyc index 3dd88f559c910ce5387a7850a10836d8cccbb337..9c49cf4b18a3d5145a45dbbbe07eac6c6c695326 100644 GIT binary patch delta 66 zcmZ24vRs6FIWI340}z~D>A#VC1`}(Ufu5oHK#Xk;;Y1yhJxqJBnxZmND!XmyHA#TIWI340}z~D>A#WtJ`-!1fu5oHK#Xk;<@JyVEUl72>hZmND!XA#Wt3=?aafu5oHK#Xk;<@4pRtY#$-cgVWH&wyyEA#V?gqgL>K+n*8atm{aT%3MJer~FMQE6UYYLR|?d45s0 Teo delta 74 zcmdm`b4Z7KIWI340|=CAZR9RtW;N6^G_si7!W^QOrk|0Yo2p+_nwOVaq#s|NUzDw% WoS#>mpOdPaSda;2Z~n&YA_M>rx)-nj diff --git a/crowdsec_service_api/services/__pycache__/vendors.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/vendors.cpython-311.pyc index d4f743a50282e1bb623c56cac65a0e44cf40b373..962fd37d415b1f37e7fc3f7b1c85efb332feff75 100644 GIT binary patch delta 66 zcmaDK@H~KfIWI340}z~D>A#Wt7&B{`fu5oH 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.17.0") def list_allowlists( self, diff --git a/crowdsec_service_api/services/blocklists.py b/crowdsec_service_api/services/blocklists.py index ac2cad5..3649a2b 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.17.0") def get_blocklists( self, diff --git a/crowdsec_service_api/services/cves.py b/crowdsec_service_api/services/cves.py index 6d90155..b103413 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.17.0") 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..43daaf2 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.17.0") 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/fingerprints.py b/crowdsec_service_api/services/fingerprints.py index 1bc0b40..a8040c8 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.17.0") 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..fef52ab 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.17.0") def get_index( self, diff --git a/crowdsec_service_api/services/info.py b/crowdsec_service_api/services/info.py index c5b2050..04b4d82 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.17.0") def get_info( self, diff --git a/crowdsec_service_api/services/integrations.py b/crowdsec_service_api/services/integrations.py index 73f5656..2a5ccec 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.17.0") def get_integrations( self, diff --git a/crowdsec_service_api/services/metrics.py b/crowdsec_service_api/services/metrics.py index 322c7c5..814fbd8 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.17.0") def get_metrics_remediation( self, diff --git a/crowdsec_service_api/services/products.py b/crowdsec_service_api/services/products.py index 5d173fc..b4f4367 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.17.0") def get_products( self, diff --git a/crowdsec_service_api/services/tracker_events.py b/crowdsec_service_api/services/tracker_events.py index fa41f55..a2ebbd3 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.17.0") 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..3da4a25 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.17.0") def get_tags( self, diff --git a/crowdsec_service_api/services/vendors.py b/crowdsec_service_api/services/vendors.py index dab3932..ce78106 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.17.0") 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/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..e81d5ac 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 @@ -273,6 +331,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: @@ -553,6 +612,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 @@ -1107,7 +1179,7 @@ name, date, description, label, sorting_priority ## Enum: INSUFFICIENT_DATA, EARLY_EXPLOITATION, FRESH_AND_POPULAR, TARGETED_EXPLOITATION, MASS_EXPLOITATION, BACKGROUND_NOISE, 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 +1202,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 +1327,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 +1404,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 +1438,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..0ce498f 100644 --- a/doc/README.md +++ b/doc/README.md @@ -39,6 +39,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) @@ -141,6 +149,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) @@ -249,7 +261,7 @@ You can find a Quickstart about this SDK, following this [documentation](https:/ [CVEExploitationPhase](./Models.md#cveexploitationphase) -[CVEResponseBase](./Models.md#cveresponsebase) +[CVEResponseDetailed](./Models.md#cveresponsedetailed) [CVEsubscription](./Models.md#cvesubscription) @@ -271,8 +283,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..a7c912e 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.47", "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-07-08T08:42:28.511313+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,7 +2756,261 @@ ], "responses": { "204": { - "description": "Successful Response" + "description": "No Content" + }, + "422": { + "description": "Unprocessable entity" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + } + }, + "/decisions/aggregated": { + "get": { + "tags": [ + "Decisions" + ], + "summary": "Get Aggregated Decisions", + "operationId": "getAggregatedDecisions", + "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", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by instance IDs", + "examples": [ + [ + "inst-abc123", + "inst-def456" + ] + ], + "default": [], + "title": "Instance Ids" + }, + "description": "Filter decisions by instance IDs" + }, + { + "name": "tag_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by tag IDs", + "examples": [ + [ + "tag-001", + "tag-002" + ] + ], + "default": [], + "title": "Tag Ids" + }, + "description": "Filter decisions by tag IDs" + }, + { + "name": "remediation_types", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by remediation types", + "examples": [ + [ + "ban", + "captcha" + ] + ], + "default": [], + "title": "Remediation Types" + }, + "description": "Filter decisions by remediation types" + }, + { + "name": "ips", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Filter decisions by IPs (only for IP decisions)", + "examples": [ + [ + "203.0.113.7", + "198.51.100.42" + ] + ], + "default": [], + "title": "Ips" + }, + "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", + "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", @@ -2710,6 +3025,85 @@ } } }, + "/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", + "required": true, + "schema": { + "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", + "required": true, + "schema": { + "type": "string", + "title": "Aggregated Id" + } + }, + { + "name": "decision_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Decision Id" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "422": { + "description": "Unprocessable entity" + }, + "404": { + "description": "Not found" + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/info": { "get": { "tags": [ @@ -3110,7 +3504,7 @@ "Cves" ], "summary": "Get list of CVEs CrowdSec is tracking", - "description": "Get a paginated list of CVEs that 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": [ { @@ -3199,6 +3593,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 +5548,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 +5616,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", @@ -5988,24 +6412,326 @@ "$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" + }, + "last_created_at": { + "type": "string", + "format": "date-time", + "title": "Last Created At", + "description": "Creation date of the last decision in the group" + }, + "first_expiration": { + "type": "string", + "format": "date-time", + "title": "First Expiration", + "description": "Expiration date of the first decision in the group" + }, + "last_expiration": { + "type": "string", + "format": "date-time", + "title": "Last Expiration", + "description": "Expiration date of the last decision in the group" + }, + "decisions": { + "items": { + "$ref": "#/components/schemas/AggregatedDecisionItem" + }, + "type": "array", + "title": "Decisions", + "description": "List of decisions in the group" + } + }, + "type": "object", + "required": [ + "id", + "scope", + "type", + "value", + "decisions" + ], + "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" + }, + "page": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Page" + }, + "size": { + "anyOf": [ + { + "type": "integer", + "minimum": 1.0 + }, + { + "type": "null" + } + ], + "title": "Size" }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } + "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": { @@ -7210,6 +7936,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, @@ -8247,6 +8979,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": { @@ -11278,7 +12039,7 @@ ], "title": "CVEExploitationPhase" }, - "CVEResponseBase": { + "CVEResponseDetailed": { "properties": { "id": { "type": "string", @@ -11421,6 +12182,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 +12252,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 +12693,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 +13035,7 @@ "properties": { "items": { "items": { - "$ref": "#/components/schemas/CVEResponseBase" + "$ref": "#/components/schemas/CVEResponseDetailed" }, "type": "array", "title": "Items" @@ -12459,7 +13144,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..e431862 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.17.0" license = { text = "MIT" } authors = [ { name="crowdsec", email="info@crowdsec.net" } diff --git a/uv.lock b/uv.lock index 2dd3cda..b15fd2c 100644 --- a/uv.lock +++ b/uv.lock @@ -13,43 +13,43 @@ wheels = [ [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.1" 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/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } 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/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, ] [[package]] name = "botocore" -version = "1.43.27" +version = "1.43.44" 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/31/9e/ad63ae4996794be10afef4e1b2f98901947da65c6c349be9c063ee73edbf/botocore-1.43.44.tar.gz", hash = "sha256:cc2a95a53efdcf0ce34a51bca28058e72fcc3b10dd625eb1ad900c5ca3eb8bef", size = 15685774, upload-time = "2026-07-09T00:38:36.71Z" } 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/71/05/5c5de6ed23b5e1b01e36b9e52f056dc2b09b259daf3371e4b615ad9a1d4a/botocore-1.43.44-py3-none-any.whl", hash = "sha256:6afb846fd93815133a53baf1578f1d6016ddbeda247623360379758ca2e3f338", size = 15374229, upload-time = "2026-07-09T00:38:33.716Z" }, ] [[package]] name = "certifi" -version = "2026.5.20" +version = "2026.6.17" 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/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } 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/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, ] [[package]] name = "crowdsec-service-api" -version = "1.128.0" +version = "0.17.0" 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]]