From a8ebdbc1bad437249209e034214247e3d1dc0cdd Mon Sep 17 00:00:00 2001 From: nitescuc <1108077+nitescuc@users.noreply.github.com> Date: Mon, 3 Aug 2026 13:30:11 +0000 Subject: [PATCH] Update python SDK 1.134.0 --- crowdsec_service_api/__init__.py | 20 +- .../__pycache__/__init__.cpython-311.pyc | Bin 5024 -> 5422 bytes .../__pycache__/base_model.cpython-311.pyc | Bin 4378 -> 4378 bytes .../__pycache__/http_client.cpython-311.pyc | Bin 7768 -> 7768 bytes .../__pycache__/models.cpython-311.pyc | Bin 158635 -> 170976 bytes crowdsec_service_api/models.py | 543 ++++- .../__pycache__/__init__.cpython-311.pyc | Bin 207 -> 207 bytes .../__pycache__/allowlists.cpython-311.pyc | Bin 12917 -> 12917 bytes .../__pycache__/blocklists.cpython-311.pyc | Bin 15121 -> 15121 bytes .../services/__pycache__/cves.cpython-311.pyc | Bin 12321 -> 12364 bytes .../__pycache__/decisions.cpython-311.pyc | Bin 4338 -> 7222 bytes .../__pycache__/engines.cpython-311.pyc | Bin 0 -> 2506 bytes .../__pycache__/fingerprints.cpython-311.pyc | Bin 12310 -> 12353 bytes .../services/__pycache__/hub.cpython-311.pyc | Bin 3277 -> 3277 bytes .../services/__pycache__/info.cpython-311.pyc | Bin 2035 -> 2035 bytes .../__pycache__/integrations.cpython-311.pyc | Bin 8075 -> 8075 bytes .../__pycache__/metrics.cpython-311.pyc | Bin 2607 -> 2607 bytes .../__pycache__/products.cpython-311.pyc | Bin 3782 -> 3782 bytes .../tracker_events.cpython-311.pyc | Bin 2914 -> 2914 bytes .../__pycache__/tracker_tags.cpython-311.pyc | Bin 5698 -> 5698 bytes .../__pycache__/vendors.cpython-311.pyc | Bin 10351 -> 10351 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..60bb3066e9774861eecf4014d7e129e585a9df33 100644 GIT binary patch delta 861 zcmZuv&ubGw6y8ZnQoBi-q}ilZTd|s!v|#Zf6r@mXsXh1u16JZ;-0r?^MmL-AW)dh; z6sZ>vLYadX58^=&Vior$3VIenFA5RCf1u#mgE*Uz#878!D{>=E~9WwJC$w2-!<*_mz4Hh?@0Yuaws<7CZ+OA#F%Q%bF?UJ#WYS* zTB+LLswn0OaDwbgjW$(28hRnG>$9p=6)5lP)0vU8SC%`9ErBqZ3%2~AfAwF+-i1>} zXCN3+w+n^5+)6&Uo#cQGW&u9|tyyx^gE_^!F!MI57z?l-wE_Z}#W5bhu`ab-U&lm$ zwABEP!~M|>@a!sbOe$FNRAxRE_>FEFfu?m+2_sC0W4hnBq2vXuq0)onh~Ewq2|WP zdd!ZSUo(d@ZjNMq!^kK)iARi4W)hF+c7v$#^r#dATr%txxy(TW^mYJ8HT2xS!nO722nv)t_ znwy(gRJmE0Zy&Ru2+Z;#HXzGSQ+e_u!9FYA;?$zD%;eN!z0AC{{333k)GYzHSZ-=b zQD$;+5f4b12QHjZnpDI)`Jj-n4zuR%H&9qUD8EN+)N)B5M%>8 I2!c%j04vLqBLDyZ diff --git a/crowdsec_service_api/__pycache__/base_model.cpython-311.pyc b/crowdsec_service_api/__pycache__/base_model.cpython-311.pyc index 98b9f58568825bc4f734f1e579444f5920d5390c..46c7336f56be204befdaa8c8a7b02e0a9bdeba3e 100644 GIT binary patch delta 20 acmbQGG)swlIWI340}vdaUa*myUl0H`M+Er* delta 20 acmbQGG)swlIWI340}wnd(b~w(F9-lM=>)C- diff --git a/crowdsec_service_api/__pycache__/http_client.cpython-311.pyc b/crowdsec_service_api/__pycache__/http_client.cpython-311.pyc index 1bc5815900c09dcc5d59b8b7d1636dda5e5ee5f9..f5f1e1300cf3cbef9467b0a7d3de3f3fc9ac7424 100644 GIT binary patch delta 20 acmca%bHj#vIWI340}vdaUa*ndR}KI}tOfJ{ delta 20 acmca%bHj#vIWI340}wnd(b~xED+d5VP6ep| diff --git a/crowdsec_service_api/__pycache__/models.cpython-311.pyc b/crowdsec_service_api/__pycache__/models.cpython-311.pyc index 6ec2ace3e093ffc22a4a8f92ab2f5aa7fa9fef13..be101f4fa8921866e09b64b8d272a30b0fba0986 100644 GIT binary patch literal 170976 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≧&`f=MPy_&SLwd6Ai4 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 diff --git a/crowdsec_service_api/models.py b/crowdsec_service_api/models.py index d2625ae..e8c5464 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-03T13:29:59+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..85dbd0592d51a6c1fe6a38d7d8664d61ae80f80a 100644 GIT binary patch delta 19 ZcmX@lc%G4aIWI340}vdaUNDjS2mmy-1*HH0 delta 19 ZcmX@lc%G4aIWI340}wnd(VED81OPLl1zG?A diff --git a/crowdsec_service_api/services/__pycache__/allowlists.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/allowlists.cpython-311.pyc index 43c12d0fa29acc733d3ff5413d13fc4bbf224419..a1658adc43cecc7af2804c6a67cf481ae3124a2d 100644 GIT binary patch delta 28 icmeyG@->BfIWI340}x!FUa*lnftktJWOFVvhduy`Z3syK delta 28 hcmeyG@->BfIWI340|=CAZRAd1W-_waoXgCi4*+<32MquK diff --git a/crowdsec_service_api/services/__pycache__/blocklists.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/blocklists.cpython-311.pyc index d87cdb1bac7f4880ebeb44a85f3d6e4d5ffdd4cb..e54637e5e811193328ef1156e50beda1ee4c087c 100644 GIT binary patch delta 28 icmbPOHnEI*IWI340}x!FUa*n-0W*`a$>z7rD&_!yJqXDF delta 28 hcmbPOHnEI*IWI340|=CAZRCEy%w%M-`7N`GIRI`;2aNy# diff --git a/crowdsec_service_api/services/__pycache__/cves.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/cves.cpython-311.pyc index 7d05d919d947b7bbc0e723946bb4f0ca957ad427..f176f38d2f3a5770637fc7032787a867acad1822 100644 GIT binary patch delta 2163 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 G?ePx+CV4#o 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..a0f3c4e4716b3088c8178dc7809bb38a772df417 100644 GIT binary patch delta 2248 zcma)6U2GFa5Z<-V_WA6yW5+*n5(kGQPA>mHKth0~DIpS6rIbHK2`U(FlNy=Wy)z|1 z((r)PsznrOCBj1=Dpf6_tyC4aFMX)WODpvO0YU;DH^61iCaW>4A&Vr0OL2 z(_4f+^h}Q8{zVMnnO5{L1n`1$d`)MGRRPT!@bQU|t-`BB!9OkQ5m>F(Ygqu-fI=Nm z2nxS?p+`XvL54PAYhYo01Ctk7P1(657Gbp|9fHOnXx-{2mDTHwaE=Izmb^+593{dU zN;>S{2>UmPgc6GZsU{%N_&%AVlN%2ZP0Mr5ddD)bDYs&6rF5Kcw6$)Qb(T#eQ%uiG znZyjsOJ?#MJKJ7aaVKTxXM$WWmWtTAOV4B$=1_kFISt1#B*! zscY(S%)925dFNNs=Mo`b!drY6+}Woy0tVf#+vZ7<&b#^J)((Ew{u@>Kgri~5Xoc0h zo6YK(baFO(^t6%6TT|&1Mi1DHZiFuMkPT*L(z7NrRZwI6x?_U+Z_0ORy@oy=2z>yW zZ1kf%fUt@G;oQP61fBiH5a@X+%jU8-WTO@rUqTp0*oIJ<&)Cl2R1Q-u_nmUc(pXB- zi>@M%vm%eDCB|P>Blaz@p6}v*74<0H zk(=^-T+ZP63eFIZ$TxN17@3yc?8psROx#vBu)U>?XOvZm%NtA`-BPEoGZ zhxxK^Kb^>p_^(>xFDwiu904M|ydzjexA5)3F;|I7FMloA%)br>G*OxVRg~r6JL=mI z)_eqTffU^QmJ;Ac{f^uRp<2t1H^6$(5WEh9sM(B8_!Bc!oF>s{F&(0kj*2!usu_iG zQHG^V+RSQ7u`n2C{b|WNGKsUPbV7%*Sq|3lcOw(@!`y>No#ol-tW4*r--YQ0>^e)a zEUW0m5zM-7C(h8%a&q*L#a`?J4>j+UZcw9-f3abK-s5et&3~G(^#8%6DFEPSf_@_ z{9el#y`KxmxwdM&Qewp`B|M&-onc1CfXi$D=@i@l?=@w-0vAY# z|I#)@|KwKf+xQrAi6JxsXw*Q1W+^xA)tdOP+T_OOz z@H)alghQOQciN{=JjR#WHTyJ*r}$6pEmion7+Hif2y=X;eSnVgo(^*{((o`4d>9B7 zykXluS|EtOd&AePmlg^(VX_l{?X|A^zP5rx=rRdL??k)q2f7PRVNghQxZo0^n|S>N zRfrxGi$SZ1v#sD0RuR9LMWB@y`g5VqBNjTq&F=G6qLp4G=|kw_e|Oi=k9fG}htaH+ V4j0JUcxCXhSo!Ne){Q7c`v;jC(l`JB delta 2132 zcma)6S!`QX5WUxVeqLX^#9O>Hb`v{(S)5H-nvJGSqgERnhky?v`hlKARf$g)8PN~K7k94P*eOZiS?_pe z?#!H-J7eEpxV9kOk|Z;NXSwZE>pB%xSA)zo1WA6bBKQE~Ev;c|k~8O$a_pGq<~* z$zP3^(raeL%@Wiz=ay}q1!7B!iii2wdCDmFs-|$Hjf$6@Iu##jfHPJ?BP+qh{rp@H zFx|}lPP1uQy?lUiAiah77e^{q#zM9UJJ)h7r9fgI3KnXKL;eSIjG>731f^rvgHsvA6Q9 zT8g%(GMlWZ=vPn%uUP0^dpo@>KEzg06s%08>xL789r;@6c+6YsF@A}Iad2$|=w=`r)gF+00s5$59vJBdaR6YD zfhH8m+2U=Z>Icghz&?OsfUVr>2wgTG$MMp4<~d!g%F_j|oXk{9#;jT&eM@SguSs^? zPv@gydPDNjQ$aIbmtqOdH3_j%fEd6o)Xw^OJX%ugZ|EXb>g`#FIP0v#Ekdu_AXndW zxH$D)hp(kdy$j*5jtqXWbliEZmT-q&spLLXAsc68e%j&o&`WL`C%oz2Cv(F8)#gUj zM+2T$aK3cTGoc%sVGSHpdBbx|l=W&m+!L%qWhwZG_;mSQ7-}a#|ME>tZjM_M^0`7$ zHdiJM*>)edX2_@B%;r)Gn>$qXe&Y#zx%7KuK=DXD4fRW-MCv~(fxf3A`|($$i;<6Os+sIiSfq?K$B`u^vzRL8)WgMMekN6X zRZX*#f{sBrtOg-}hJM(xAOAuhw(L|j{7eFLEo!3E(aKL{XosqsA>nC&UG8rq`ZE5Z zl!(RtHK{H4Mf34q%jTriL3$@1!V~n{_%!~F`sI7EgTNC8h%k_`3IkM}z!~am z@56ocXnSGJ<^Iv-DN7#x3@#(UM>g;Ex_4H~dTtVt(|^75fg@fvaNUU9q3=VT4_t|| zi5tww<}6#dXa%v7sUDf)WgEBhq?Oc9u-mB*J#zcXdiGs9+VO%8_tK9N=ViW9@Qrc~+8pIWI340}x!FUa*n7nu*ESWOE0TD;EHCga@wx delta 28 hcmX>rc~+8pIWI340|=CAZRDtSI?d$+=Z3pN8 delta 28 hcmZ24vR;IHIWI340|=CAZRDQK#AIZ#c?DBDI{;mK2CM)8 diff --git a/crowdsec_service_api/services/__pycache__/products.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/products.cpython-311.pyc index 46ae2eb243d215432d14d619a14b512bbe5e3d54..c73892dd0db7a259104ba704a50cc9d984cc0f36 100644 GIT binary patch delta 28 icmX>mdrX#lIWI340}x!FUa*n-5fhWK$>#S=*SP_Dp$Q5A delta 28 hcmX>mdrX#lIWI340|=CAZRCE$#AIZ#`90HhZUAVn2hIQh 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..620f9ecd36c6ebe1f3b50ca67af46392c806c008 100644 GIT binary patch delta 28 icmaDP_DGC-IWI340}x!FUa*n-JQI_#$>uvuZ5#l7ZV0*n delta 28 hcmaDP_DGC-IWI340|=CAZR9@B#AIZ#`3_SX2LNk)2Yvtm diff --git a/crowdsec_service_api/services/__pycache__/tracker_tags.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/tracker_tags.cpython-311.pyc index 7aeca28cde4a0ff40e041360eb356d7d52e08cab..fa778218aa0e63820d870c219ecc5b12fce60dfb 100644 GIT binary patch delta 28 icmX@4b4Z7KIWI340}x!FUa*n7oSDhkWOEDiJ|O^h+z2`V delta 28 hcmX@4b4Z7KIWI340|=CAZR9RzW-_wa+`_z12moa22L1p5 diff --git a/crowdsec_service_api/services/__pycache__/vendors.cpython-311.pyc b/crowdsec_service_api/services/__pycache__/vendors.cpython-311.pyc index d4f743a50282e1bb623c56cac65a0e44cf40b373..cb218e626b3b09ceb42b280810f5305ab23c93f0 100644 GIT binary patch delta 28 icmaDK@IHWhIWI340}x!FUa*n-Br}t-$>wX!GgSbK?g?!G delta 28 hcmaDK@IHWhIWI340|=CAZR9@5%w%M-`5N;~6##ks2r&Qv diff --git a/crowdsec_service_api/services/allowlists.py b/crowdsec_service_api/services/allowlists.py index 057a909..39794b1 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/1.134.0") def list_allowlists( self, diff --git a/crowdsec_service_api/services/blocklists.py b/crowdsec_service_api/services/blocklists.py index ac2cad5..16142ca 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/1.134.0") def get_blocklists( self, diff --git a/crowdsec_service_api/services/cves.py b/crowdsec_service_api/services/cves.py index 6d90155..13fc422 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/1.134.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..2234050 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/1.134.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/engines.py b/crowdsec_service_api/services/engines.py new file mode 100644 index 0000000..dce3c4a --- /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/1.134.0") + + 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..d1b83d6 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/1.134.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..a1cea62 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/1.134.0") def get_index( self, diff --git a/crowdsec_service_api/services/info.py b/crowdsec_service_api/services/info.py index c5b2050..59035a2 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/1.134.0") def get_info( self, diff --git a/crowdsec_service_api/services/integrations.py b/crowdsec_service_api/services/integrations.py index 73f5656..f4b15ee 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/1.134.0") def get_integrations( self, diff --git a/crowdsec_service_api/services/metrics.py b/crowdsec_service_api/services/metrics.py index 322c7c5..28775d7 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/1.134.0") def get_metrics_remediation( self, diff --git a/crowdsec_service_api/services/products.py b/crowdsec_service_api/services/products.py index 5d173fc..8b13ab4 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/1.134.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..d2cc05f 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/1.134.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..fb869e3 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/1.134.0") def get_tags( self, diff --git a/crowdsec_service_api/services/vendors.py b/crowdsec_service_api/services/vendors.py index dab3932..d68fd4c 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/1.134.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/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..bca9d39 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.54", "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..f680735 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 = "1.134.0" license = { text = "MIT" } authors = [ { name="crowdsec", email="info@crowdsec.net" } diff --git a/uv.lock b/uv.lock index 2dd3cda..dc9d744 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.62" 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/4e/8d/36af6d99269a701f83809b87a01f4728699eb825ebdedee3a3d515b18f61/botocore-1.43.62.tar.gz", hash = "sha256:94efc419c9f0f41dc2415e4b6b62f04ae21b3ce3930fac47214c4d3f361ea8b8", size = 15818261, upload-time = "2026-07-31T19:35:06.235Z" } 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/c0/65/d5dae96de68ffc55acf87c3bae76e9dabeeca92aadf1223f20e9a7860aef/botocore-1.43.62-py3-none-any.whl", hash = "sha256:76de153de1ba3e242b2e6df6a13ab8a3fb35d17db562462969e661457b63166e", size = 15502622, upload-time = "2026-07-31T19:35:02.697Z" }, ] [[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 = "1.134.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]] @@ -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]]